简体   繁体   English

如何为类型引用的操作指定通用特征?

[英]How do I specify a generic trait for operations on references to types?

I'm trying to create a trait abstracting over generic number types.我正在尝试创建一个对通用数字类型进行抽象的特征。 Specifying that I want the trait to require T + T (ie T: Add<T> ) is easy.指定我希望特征需要T + T (即T: Add<T> )很容易。 I can even specify T + &T in the trait and then use that in generic functions on some type that implements that trait.我什至可以在特征中指定T + &T ,然后在实现该特征的某种类型的泛型函数中使用它。

I'm running into problems however when I try to add a constraint for &T + T (ie for<'a> &'a Self: Add<Self> ) and &T + &T to the trait.但是,当我尝试为&T + T (即for<'a> &'a Self: Add<Self> )和&T + &T添加约束时遇到了问题。 Strangely I can specify these same constraints without any issues when writing a blanket impl for the numeric trait, and also when writing a generic function for any type of that trait, but I can't get the compiler to let me just specify these constraints once on the trait instead.奇怪的是,在为数字特征编写全面impl时,以及在为该特征的任何类型编写通用函数时,我可以毫无问题地指定这些相同的约束,但是我无法让编译器让我只指定一次这些约束而是在特质上。

Simplified example简化示例

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);
}

I suppose I've managed to answer my own question after staring at how num-traits did it for quite a while and trail and error.我想我在盯着num-traits如何做到这一点很长一段时间以及跟踪和错误之后设法回答了我自己的问题。

This altered version of my initial example creates two extra traits to allow you to get very close to the original goal.我最初示例的这个修改版本创建了两个额外的特征,使您能够非常接近原始目标。 Although you still need a where clause it will not increase in size as you add more constraints for other operations.尽管您仍然需要一个 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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM