Require commutative operation in Rust trait bound - rust

Suppose I have a group of related non-scalar structs with a commutative arithmetic operation defined on them. For example,
struct Foo {
a: f64,
b: f64
}
impl Add<f64> for Foo {
type Output = Foo;
fn add(self, v: f64) -> Self::Output {
Foo {
a: self.a + v,
b: self.b + v
}
}
}
impl Add<Foo> for f64 {
type Output = Foo;
fn add(self, foo: Foo) -> Self::Output {
Foo {
a: foo.a + self,
b: foo.b + self
}
}
}
I want to implement a trait on this group of structs, taking advantage of this operation. That is, I want something like the following:
trait Bar: Add<f64, Output = Self> + Sized {
fn right_add(self, f: f64) -> Self {
self + f
}
// Doesn't compile!
fn left_add(self, f: f64) -> Self {
f + self
}
}
However, this currently doesn't compile, since the super-trait bound doesn't include the left addition of f64 to Self. My question is: How can I state this commutative trait bound?
(Playground link.)
Edit: To be clear, I'm aware that right_add and left_add have the same output. I'm mainly interested in the ergonomics of not having to remember which is "correct" according to the compiler. In addition, I'm curious to learn how to do this, even if it's not strictly necessary.

Inverted trait bounds like this are the exact usecase for where syntax:
trait Bar
where
f64: Add<Self, Output = Self>,
Self: Add<f64, Output = Self> + Sized,
{
fn right_add(self, f: f64) -> Self {
self + f
}
fn left_add(self, f: f64) -> Self {
f + self
}
}
Playground link

Related

Implementing Add, Sub, Mul, Div for all combinations of move and borrow

I have a simple struct that should implement basic math operators. Initially I didn't want these to consume the operands, so I implemented the traits for references, for example for Add:
impl<'a, 'b> Add<&'b MyType> for &'a MyType {
type Output = MyType;
fn add(self, rhs: &'b MyType) -> Self::Output {
// Implementation...
}
}
This allows me to do:
let result = &v1 + &v2;
Where v1 and v2 are of type MyType.
Then I realized that sometimes it is syntactically more convenient to consume the operands, for example when doing:
let result = &v1 + &v2 + &v3;
Because there is an intermediate result the above won't compile and you have to do:
let result = &v1 + &(&v2 + &v3);
So I ended up implementing the other permutations of move and borrow, which just defer to the first one:
impl<'a> Add<&'a MyType> for MyType {
type Output = MyType;
fn add(self, rhs: &'a MyType) -> Self::Output {
&self + rhs
}
}
impl<'a> Add<MyType> for &'a MyType {
type Output = MyType;
fn add(self, rhs: MyType) -> Self::Output {
self + &rhs
}
}
impl Add<MyType> for MyType {
type Output = MyType;
fn add(self, rhs: MyType) -> Self::Output {
&self + &rhs
}
}
This works, but is cumbersome.
I looked for an easier way, such as using Borrow<T>:
impl<B> Add<B> for B
where
B: Borrow<MyType>,
{
type Output = MyType;
fn add(self, rhs: B) -> Self::Output {
// Implementation...
}
}
But that won't compile, understandably, due to type parameter B must be used as the type parameter for some local type.
Are there any other tricks to avoid having all these boilerplate implementations for Add/Sub/Mul/Div, etc?
Update:
#EvilTak made a suggestion in the comments which cuts down on the boilerplate combinations by implementing the B: Borrow<MyType> version on MyType and &MyType explicitly. This is a good improvement:
impl<'a, B> Add<B> for &'a MyType
where
B: Borrow<MyType>,
{
type Output = MyType;
fn add(self, rhs: B) -> Self::Output {
// Implementation...
}
}
impl<B> Add<B> for MyType
where
B: Borrow<MyType>,
{
type Output = MyType;
fn add(self, rhs: B) -> Self::Output {
&self + rhs
}
}
Having gone down this rabbit hole, I'll answer my own question.
I started off using Borrow to reduce the number of functions I would need to implement:
impl<'a, B> Add<B> for &'a MyType
where
B: Borrow<MyType>,
{
type Output = MyType;
fn add(self, rhs: B) -> Self::Output {
// Implementation...
}
}
impl<B> Add<B> for MyType
where
B: Borrow<MyType>,
{
type Output = MyType;
fn add(self, rhs: B) -> Self::Output {
&self + rhs
}
}
This worked well until I also needed to add MyType and MyOtherType together.
Attempting to implement that using Borrow<T> gave the error conflicting implementations of trait:
impl<'a, B> Mul<B> for &'a MyType
where
B: Borrow<MyOtherType>,
{
type Output = MyOtherType;
fn mul(self, rhs: B) -> Self::Output {
/// Implementation...
}
}
This is because a type could theoretically implement both Borrow<MyType> and Borrow<MyOtherType> at the same time, and the compiler wouldn't know which implementation to use.
At that point I decided to try the macro route instead. As you would expect, this has been done before by others.
A couple of different places suggested using impl_ops, which has since been replaced by auto_ops.
This crate lets you define all the various combinations for an operator by doing something like:
impl_op_ex!(+ |a: &DonkeyKong, b: &DonkeyKong| -> DonkeyKong { DonkeyKong::new(a.bananas + b.bananas) });
However, this crate has the limitation of not working with generics. In my case MyType is actually Matrix<const M: usize, const N: usize>, so I needed generics support.
I then came across auto_impl_ops, which lets you generate all the various add combinations from a single AddAsign trait implementation (and same for other ops), and also supports generics.
use std::ops::*;
#
# #[derive(Clone, Default)]
# struct A<T>(T);
#[auto_impl_ops::auto_ops]
impl<M> AddAssign<&A<M>> for A<M>
where
for<'x> &'x M: Add<Output = M>,
{
fn add_assign(&mut self, other: &Self) {
self.0 = &self.0 + &other.0;
}
}
One limitation here is that the result always has to be the same type as Self, which may not be the case if you are doing, for example, a matrix multiplied by a vector.
Another possible issue is that for a binary operator the crate will clone the left-hand value before using your assignment operator implementation, returning the clone. For matrix multiplication I also had to then clone self in the MulAssign implementation, or I would be overwriting the data I'm still using for the matrix multiplication. This means there is at least one redundant memory copy here, which I wouldn't have if I implemented the operator manually.
I've gone with this library for now. I'll try and update here if that changes.

Create method on iterator that returns iterator in Rust

I want to define a lazy square() method without unnecessary runtime overhead (no dyn keyword) that can be called on any Iterable<Item = u8> and returns another Iterable<Item = u8>, like so:
fn main() {
vec![1, 2, 3, 4, 5]
.iter()
.filter(|x| x > 1)
.squared()
.filter(|x| x < 20);
}
I know how to define squared() as a standalone function:
fn squared<I: Iterator<Item = u8>>(iter: I) -> impl Iterator<Item = u8> {
iter.map(|x| x * x)
}
To define that method on Iterator<Item = u8> though, I have to first define a trait.
Here's where I struggle — traits cannot use the impl keyword in return values.
I'm looking for something like the following, which does not work:
trait Squarable<I: Iterator<Item = u8>> {
fn squared(self) -> I;
}
impl<I, J> Squarable<I> for J
where
I: Iterator<Item = u8>,
J: Iterator<Item = u8>,
{
fn squared(self) -> I {
self.map(|x| x * x)
}
}
I had many failed attempts at solving the problem, including changing the return type of squared to Map<u8, fn(u8) -> u8> and tinkering with IntoIterables, but nothing worked so far. Any help would be greatly appreciated!
First of all, your output iterator should probably be an associated type and not a trait parameter, since that type is an output of the trait (it's not something that the caller can control).
trait Squarable {
type Output: Iterator<Item = u8>;
fn squared(self) -> I;
}
That being said, there are a few different possible approaches to solve this problem, each with different advantages and disadvantages.
Using trait objects
The first is to use trait objects, e.g. dyn Iterator<Item = u8>, to erase the type at runtime. This comes at a slight runtime cost, but is definitely the simplest solution in stable Rust today:
trait Squarable {
fn squared(self) -> Box<dyn Iterator<Item = u8>>;
}
impl<I: 'static + Iterator<Item = u8>> Squarable for I {
fn squared(self) -> Box<dyn Iterator<Item = u8>> {
Box::new(self.map(|x| x * x))
}
}
Using a custom iterator type
In stable rust, this is definitely the cleanest from the point of view of the user of the trait, however it takes a bit more code to implement because you need to write your own iterator type. However, for a simple map iterator this is pretty straight forward:
trait Squarable: Sized {
fn squared(self) -> SquaredIter<Self>;
}
impl<I: Iterator<Item = u8>> Squarable for I {
fn squared(self) -> SquaredIter<I> {
SquaredIter(self)
}
}
struct SquaredIter<I>(I);
impl<I: Iterator<Item = u8>> Iterator for SquaredIter<I> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
self.0.next().map(|x| x * x)
}
}
Using the explicit Map type
<I as Iterator>::map(f) has a type std::iter::Map<I, F>, so if the type F of the mapping function is known, we can use that type explicitly, at no runtime cost. This does expose the specific type as part of the function's return type though, which makes it harder to replace in the future without breaking dependent code. In most cases the function will also not be known; in this case we can use F = fn(u8) -> u8 however since the function does not keep any internal state (but often that won't work).
trait Squarable: Sized {
fn squared(self) -> std::iter::Map<Self, fn(u8) -> u8>;
}
impl<I: Iterator<Item = u8>> Squarable for I {
fn squared(self) -> std::iter::Map<Self, fn(u8) -> u8> {
self.map(|x| x * x)
}
}
Using an associated type
An alterative to the above is to give the trait an assoicated type. This still has the restriction that the function type must be known, but it's a bit more general since the Map<...> type is tied to the implementation instead of the trait itself.
trait Squarable {
type Output: Iterator<Item = u8>;
fn squared(self) -> Self::Output;
}
impl<I: Iterator<Item = u8>> Squarable for I {
type Output = std::iter::Map<Self, fn(u8) -> u8>;
fn squared(self) -> Self::Output {
self.map(|x| x * x)
}
}
Using impl in associated type
This is similar to the "Using an associated type" above, but you can hide the actual type entirely, apart from the fact that it is an iterator. I personally think this is the preferrable solution, but unfortunately it is still unstable (it depends on the type_alias_impl_trait feature) so you can only use it in nightly Rust.
#![feature(type_alias_impl_trait)]
trait Squarable {
type Output: Iterator<Item = u8>;
fn squared(self) -> Self::Output;
}
impl<I: Iterator<Item = u8>> Squarable for I {
type Output = impl Iterator<Item = u8>;
fn squared(self) -> Self::Output {
self.map(|x| x * x)
}
}

How to combine all operator overloading combinations into a single trait in Rust?

Is there a good way to combine all operator overloading combinations into a single trait in Rust? As discussed, in the question How to implement idiomatic operator overloading for values and references in Rust?, there are four different combinations for operator overloading binary operators: (ref,ref), (ref,val), (val,ref), and (val,val). Further, there's a number of macros and such for helping define these routines. My question is how to require all of these combinations in a sane way through a trait or another mechanism. Consider the following code that attempts to create an algebra for a general float/real type:
// Create some random type that we want to represent as a Real
#[derive(Debug,Clone)]
struct Foo <Real> {
x : Real,
y : Real,
}
// Add the algebra for Foo
impl <Real> std::ops::Add <&'_ Foo<Real>> for &'_ Foo <Real>
where
for <'a> &'a Real : std::ops::Add<&'a Real,Output=Real>
{
type Output = Foo <Real>;
fn add(self, other : &'_ Foo <Real>) -> Self::Output {
Foo {
x : &self.x + &other.x,
y : &self.y + &other.y,
}
}
}
impl <Real> std::ops::Add <Foo<Real>> for &'_ Foo <Real>
where
for <'a> &'a Real : std::ops::Add<Real,Output=Real>
{
type Output = Foo <Real>;
fn add(self, other : Foo <Real>) -> Self::Output {
Foo {
x : &self.x + other.x,
y : &self.y + other.y,
}
}
}
impl <Real> std::ops::Add <&'_ Foo<Real>> for Foo <Real>
where
for <'a> Real : std::ops::Add<&'a Real,Output=Real>
{
type Output = Foo <Real>;
fn add(self, other : &'_ Foo <Real>) -> Self::Output {
Foo {
x : self.x + &other.x,
y : self.y + &other.y,
}
}
}
impl <Real> std::ops::Add <Foo<Real>> for Foo <Real>
where
Real : std::ops::Add<Real,Output=Real>
{
type Output = Foo <Real>;
fn add(self, other : Foo <Real>) -> Self::Output {
Foo {
x : self.x + other.x,
y : self.y + other.y,
}
}
}
// Compute a function on a slice of Reals. This should work for f64 and Foo <f64>
fn foo <Real> (x : &[Real]) -> Real
where
for <'a> &'a Real :
std::ops::Add<&'a Real,Output=Real> +
std::ops::Add<Real,Output=Real> +
Clone,
for <'a> Real :
std::ops::Add<&'a Real,Output=Real> +
std::ops::Add<Real,Output=Real> +
Clone,
Real : Clone
{
(&x[0]+x[1].clone())+&x[2]
}
// Run foo on two different types
fn main() {
let x = vec![1.2,2.3,3.4];
let _x = foo::<f64>(&x);
println!("{:?}",_x);
let y : Vec <Foo<f64>>= x.into_iter().map(|z|Foo{x:z,y:z+1.0}).collect();
let _y = foo::<Foo<f64>>(&y);
println!("{:?}",_y);
}
The routine foo places a requirement both on Real and its reference for <'a> &'a Real to complete the algebra. Now, this is pretty verbose and will only get worse as we add Mul, Div, etc. I'd like to either have a single trait for Real or at least a single trait for Real and for <'a> &'a Real. That said, I can't quite figure out a definition for it. Typically, I'd place all of the definitions in a new trait such as:
trait MyFloat :
std::ops::Add <REFSELF,Output = NOREFSELF> +
std::ops::Add <NOREFSELF,Output = NOREFSELF>
where
Self : std::marker::Sized,
{}
impl <T> MyFloat for T where T:
std::ops::Add <REFSELF,Output = NOREFSELF> +
std::ops::Add <NOREFSELF,Output = NOREFSELF>
{}
But, this is already getting odd. We need the output to always be the non-reference form of the type and we want the right hand side to be either the reference form or the non-reference form of the type. However, as far as I know, there's not a mechanism for achieving this by manipulating Self.
Is there a good way for combining these definitions into a single trait?

Strange behavior of HRTBs

I have this code:
use std::fmt::Debug;
struct S<A>
where
for<'a> A: Debug + 'a,
{
f: Box<Fn(A) -> i32>,
}
impl<A> S<A>
where
for<'a> A: Debug + 'a,
{
fn call(&self, a: A) {
println!("Return {:?}", (self.f)(a));
}
}
fn create<A>(f: Box<Fn(A) -> i32>) -> S<A>
where
for<'a> A: Debug + 'a,
{
S::<A> { f }
}
fn helper() {
let x = create::<&i32>(Box::new(|x: &i32| *x * 2));
let arg = 333;
x.call(&arg);
}
fn main() {
let x = helper();
}
It's failed to compile:
error[E0310]: the parameter type `A` may not live long enough
In code 2, I changed Fn(A) -> i32 to Fn(&A) -> i32, the code works.
...
f: Box<Fn(&A) -> i32>,
...
Since A is argument of Fn trait, it's a type that has Higher-Rank lifetime. It shouldn't be affected by the lifetime of struct S<A> .
But why can't code 1 be compiled?
How can I workaround it for borrow or non-borrow type A?
There is no easy way to make helper work in current Rust, even if you remove all the for<'a> A: Debug + 'a, bounds (which only further restricts what types A can be, whereas you want to allow more).
This is as simple as I can make your example:
struct S<A> {
f: Box<Fn(A) -> i32>,
}
impl<A> S<A> {
fn call(&self, a: A) {
println!("Return {:?}", (self.f)(a));
}
}
fn create<A>(f: Box<Fn(A) -> i32>) -> S<A> {
S { f }
}
fn helper() {
let x = create(Box::new(|x: &i32| *x * 2));
let arg = 333;
x.call(&arg);
}
fn main() {
helper();
}
The reason it doesn't work is that A "comes from the outside", and Rust can't infer that you want for<'a> S<&'a A>, it can't even talk about such a type.
Note that if let arg = 333; is placed above let x, this example does compile (because it infers a reference to arg specifically, not a for<'a>).
The closest you can get today is with an associated type on a trait with a lifetime parameter, e.g.:
// Emulating `type Type<'a>` by moving `'a` to the trait.
trait Apply<'a> {
type Type;
}
struct Plain<T>(std::marker::PhantomData<T>);
impl<'a, T> Apply<'a> for Plain<T> {
type Type = T;
}
struct Ref<T: ?Sized>(std::marker::PhantomData<T>);
impl<'a, T: ?Sized + 'a> Apply<'a> for Ref<T> {
type Type = &'a T;
}
struct S<A: for<'a> Apply<'a>> {
f: Box<for<'a> Fn(<A as Apply<'a>>::Type) -> i32>,
}
impl<A: for<'a> Apply<'a>> S<A> {
fn call<'a>(&self, a: <A as Apply<'a>>::Type) {
println!("Return {:?}", (self.f)(a));
}
}
fn create<A: for<'a> Apply<'a>>(
f: Box<for<'a> Fn(<A as Apply<'a>>::Type) -> i32>,
) -> S<A> {
S { f }
}
fn helper() {
let x = create::<Ref<i32>>(Box::new(|x: &i32| *x * 2));
let arg = 333;
x.call(&arg);
}
fn main() {
helper();
}
However, it turns out that this encoding hits https://github.com/rust-lang/rust/issues/52812, so it's not actually usable at the moment (and I'm not aware of an workaround).

How can I reuse code for similar trait implementations?

I have a few traits with default implementations that require the same methods, which happen to be field getters.
trait AddPosition<T: Display>{
fn x(&self) -> T;
fn y(&self) -> T;
fn add(&self){
println!("{:}", self.x()+self.x());
}
}
trait SubPosition<T: Display>{
fn x(&self) -> T;
fn y(&self) -> T;
fn sub(&self){
println!("{:}", self.x()-self.y());
}
}
Instead of manually repeating the same code for each trait implementation, can I have something like this?
impl AddPosition<i32>, SubPosition<i32> for Point{
fn x(&self) -> i32{ self.x }
fn y(&self) -> i32{ self.y }
}
The best option is probably to factor out the fact that your objects can have a position:
trait Position<T: Display> {
fn x(&self) -> T;
fn y(&self) -> T;
}
// trait AAA: BBB --> means AAA must implement BBB
trait AddPosition<T: Add<Output=T> + Display>: Position<T> {
fn add(&self){
println!("{:}", self.x()+self.x()); // ?
}
}
trait SubPosition<T: Sub<Output=T> + Display>: Position<T> {
fn sub(&self){
println!("{:}", self.x()-self.y()); // ?
}
}
struct MyPosition {
x: i32,
y: i32,
}
impl Position<i32> for MyPosition {
fn x(&self) -> i32 { self.x }
fn y(&self) -> i32 { self.y }
}
impl SubPosition<i32> for MyPosition {}
impl AddPosition<i32> for MyPosition {}
(Playground)
However, I fail to understand how your code really makes sense (? annotated lines). If this is just for the sake of the minimal example, this is totally fine; however, if this is meant for any serious code, you may want to look into the Add and Sub trait, which will allow you to benefit from operator overloading + and -. Even if you don't use those traits directly, they may inspire you for meaningful signatures of a potential add(&self, rhs: &P) -> P function (where P: Position<T>).

Resources