Ske*_*nxf 8 generics traits rust
我正在尝试创建一个对通用数字类型进行抽象的特征。指定我希望 trait 需要T + T(即T: Add<T>)很容易。我什至可以指定T + &T在特征中,然后在实现该特征的某种类型的泛型函数中使用它。
然而,当我尝试为&T + T(ie for<'a> &'a Self: Add<Self>) 和&T + &T特征添加约束时,我遇到了问题。奇怪的是impl,在为数字特征编写毯子时,以及在为该特征的任何类型编写通用函数时,我可以毫无问题地指定这些相同的约束,但是我无法让编译器让我只指定这些约束一次相反的特质。
简化示例
use std::ops::Add;
trait Numeric where
Self: Sized,
// T + T
Self: Add<Output = Self>,
// T + &T
Self: for<'a> Add<&'a Self, Output = Self>,
// &T + T
// should specify &T + T on this trait but instead causes compile
// errors everywhere I try to use Numeric claiming no implementation
// for &T + T
// for<'a> &'a Self: Add<Self, Output = Self>,
// &T + &T
// should specify &T + &T on this trait but instead causes compile
// errors everywhere I try to use Numeric claiming no implementation
// for &T + &T
// for<'a, 'b> &'a Self: Add<&'b Self, Output = Self>,
{}
impl <T> Numeric for T where
// T + T
T: Add<Output = Self>
+ Sized,
// T + &T
for<'a> T: Add<&'a T, Output = T>,
// &T + T
for<'a> &'a T: Add<T, Output = T>,
// &T + &T
for<'a, 'b> &'a T: Add<&'b T, Output = T>,
{}
// works
fn generic_add_0<T: Numeric>(a: T, b: T) -> T {
a + b
}
// works
fn generic_add_1<T: Numeric>(a: T, b: &T) -> T {
a + b
}
// doesn't infer &T + T from the trait?
fn generic_add_2<T: Numeric>(a: &T, b: T) -> T {
a + b
}
// doesn't infer &T + &T from the trait?
fn generic_add_3<T: Numeric>(a: &T, b: &T) -> T {
a + b
}
// works
fn generic_add_4<T: Numeric>(a: &T, b: &T) -> T
// I want to not have to specify this every time I use Numeric,
// I want Numeric to imply this constraint for me
where for<'a, 'b> &'a T: Add<&'b T, Output = T> {
a + b
}
fn main() {
generic_add_0(1.0, 2.0);
generic_add_1(1.0, &2.0);
generic_add_2(&1.0, 2.0);
generic_add_3(&1.0, &2.0);
generic_add_4(&1.0, &2.0);
}
Run Code Online (Sandbox Code Playgroud)
我想在盯着num-traits它是如何做到相当长一段时间以及尝试和错误之后,我已经设法回答了我自己的问题。
我最初的示例的修改版本创建了两个额外的特征,使您能够非常接近最初的目标。尽管您仍然需要一个 where 子句,但当您为其他操作添加更多约束时,它的大小不会增加。
use std::ops::Add;
/**
* Trait defining what a numeric type is, in this case just something
* that can be added to a right hand side and yield itself as output
*/
trait NumericByValue<Rhs = Self, Output = Self>: Sized + Add<Rhs, Output = Output> {}
/**
* All types implemeting the by value operations are NumericByValue
*/
impl <T, Rhs, Output> NumericByValue<Rhs, Output> for T where
T: Add<Rhs, Output = Output> {}
/**
* The trait to define &T op T and &T op &T versions for NumericByValue
* based off the MIT/Apache 2.0 licensed code from num-traits 0.2.10
* http://opensource.org/licenses/MIT
* https://docs.rs/num-traits/0.2.10/src/num_traits/lib.rs.html#112
*
* The trick is that all types implementing this trait will be references,
* so the first constraint expresses some &T which can be operated on with
* some right hand side type T to yield a value of type T.
*
* In a similar way the second constraint expresses &T op &T -> T operations
*/
trait NumericRef<T>:
// &T op T -> T
NumericByValue<T, T>
// &T op &T -> T
+ for<'a> NumericByValue<&'a T, T> {}
/**
* All types implementing the operations from NumericByValue by reference,
* are NumericRef<T>, ie a type like &u8 is NumericRef<u8>.
*/
impl <RefT, T> NumericRef<T> for RefT where
RefT: NumericByValue<T, T>
+ for<'a> NumericByValue<&'a T, T> {}
/**
* A trait extending the constraints in NumericByValue to
* types which also support the operations with a right hand side type
* by reference.
*
* When used together with NumericRef this can express all 4 by value
* and by reference combinations for the operations using the
* following
*
* ```ignore
* fn function_name<T: Numeric>()
* where for<'a> &'a T: NumericRef<T> {
* ```
*/
trait Numeric: NumericByValue + for<'a> NumericByValue<&'a Self> {}
/**
* All types implemeting the operations in NumericByValue with a right hand
* side type by reference are Numeric.
*/
impl <T> Numeric for T where T: NumericByValue + for<'a> NumericByValue<&'a T> {}
fn generic_add_0<T: Numeric>(a: T, b: T) -> T
where for<'a> &'a T: NumericRef<T> {
a + b
}
fn generic_add_1<T: Numeric>(a: T, b: &T) -> T
where for<'a> &'a T: NumericRef<T> {
a + b
}
fn generic_add_2<T: Numeric>(a: &T, b: T) -> T
where for<'a> &'a T: NumericRef<T> {
a + b
}
fn generic_add_3<T: Numeric>(a: &T, b: &T) -> T
where for<'a> &'a T: NumericRef<T> {
a + b
}
fn main() {
generic_add_0(1.0, 2.0);
generic_add_1(1.0, &2.0);
generic_add_2(&1.0, 2.0);
generic_add_3(&1.0, &2.0);
}
Run Code Online (Sandbox Code Playgroud)