Каковы различные стратегии для разрешения привязки рекурсивного типа? - PullRequest
1 голос
/ 14 октября 2019

У меня есть следующий код, который пытается создать составную черту, которая включает все различные комбинации значения / ссылки для метода двух аргументов.

#![recursion_limit = "10"]

// Create a trait with some methods
pub trait Methods2<Other> {
    type Output;
    fn min(self, other: Other) -> Self::Output;
}

// Create a composite trait that contain multiple traits
trait AllCombos<NonRef>:
    Methods2<NonRef, Output = NonRef> + for<'a> Methods2<&'a NonRef, Output = NonRef>
{
}

impl<T, NonRef> AllCombos<NonRef> for T where
    T: Methods2<NonRef, Output = NonRef> + for<'a> Methods2<&'a NonRef, Output = NonRef>
{
}

// Implement this trait for f32
impl Methods2<f32> for f32 {
    type Output = f32;
    fn min(self, other: f32) -> Self::Output {
        self.min(other)
    }
}

impl Methods2<f32> for &f32 {
    type Output = f32;
    fn min(self, other: f32) -> Self::Output {
        (*self).min(other)
    }
}

impl Methods2<&f32> for f32 {
    type Output = f32;
    fn min(self, other: &f32) -> Self::Output {
        self.min(*other)
    }
}

impl Methods2<&f32> for &f32 {
    type Output = f32;
    fn min(self, other: &f32) -> Self::Output {
        (*self).min(*other)
    }
}

// Create a struct with a generic float inside
#[derive(Debug)]
struct MyStruct<Float> {
    x: Float,
}

// Implement to Methods2 trait for MyStruct
impl<Float> Methods2<MyStruct<Float>> for MyStruct<Float>
where
    Float: AllCombos<Float>,
    for<'a> &'a Float: AllCombos<Float>,
{
    type Output = MyStruct<Float>;
    fn min(self, other: MyStruct<Float>) -> Self::Output {
        MyStruct::<Float> {
            x: self.x.min(other.x),
        }
    }
}

impl<Float> Methods2<MyStruct<Float>> for &MyStruct<Float>
where
    Float: AllCombos<Float>,
    for<'a> &'a Float: AllCombos<Float>,
{
    type Output = MyStruct<Float>;
    fn min(self, other: MyStruct<Float>) -> Self::Output {
        MyStruct::<Float> {
            x: (&self.x).min(other.x),
        }
    }
}

impl<Float> Methods2<&MyStruct<Float>> for MyStruct<Float>
where
    Float: AllCombos<Float>,
    for<'a> &'a Float: AllCombos<Float>,
{
    type Output = MyStruct<Float>;
    fn min(self, other: &MyStruct<Float>) -> Self::Output {
        MyStruct::<Float> {
            x: self.x.min(&other.x),
        }
    }
}

impl<Float> Methods2<&MyStruct<Float>> for &MyStruct<Float>
where
    Float: AllCombos<Float>,
    for<'a> &'a Float: AllCombos<Float>,
{
    type Output = MyStruct<Float>;
    fn min(self, other: &MyStruct<Float>) -> Self::Output {
        MyStruct::<Float> {
            x: (&self.x).min(&other.x),
        }
    }
}

// Lifts a variable into MyStruct
fn lift<Float>(x: Float) -> MyStruct<Float>
where
    Float: AllCombos<Float>,
    for<'a> &'a Float: AllCombos<Float>,
{
    MyStruct { x }
}

// Create an element
fn main() {
    let bar = lift(4.0_f32).min(lift(2.0_f32));
    //let bar = lift::<f32>(4.0_f32).min(lift::<f32>(2.0_f32));
    println!("{:?}", bar.x);
}

Это приводит к ошибке компилятора

error[E0275]: overflow evaluating the requirement `&'a MyStruct<_>: Methods2<MyStruct<_>>`
   --> src/main.rs:119:15
    |
119 |     let bar = lift(4.0_f32).min(lift(2.0_f32));
    |               ^^^^
    |
    = help: consider adding a `#![recursion_limit="20"]` attribute to your crate
    = note: required because of the requirements on the impl of `for<'a> AllCombos<MyStruct<_>>` for `&'a MyStruct<_>`
    = note: required because of the requirements on the impl of `Methods2<MyStruct<MyStruct<_>>>` for `&'a MyStruct<MyStruct<_>>`
    = note: required because of the requirements on the impl of `for<'a> AllCombos<MyStruct<MyStruct<_>>>` for `&'a MyStruct<MyStruct<_>>`
    = note: required because of the requirements on the impl of `Methods2<MyStruct<MyStruct<MyStruct<_>>>>` for `&'a MyStruct<MyStruct<MyStruct<_>>>`
    = note: required because of the requirements on the impl of `for<'a> AllCombos<MyStruct<MyStruct<MyStruct<_>>>>` for `&'a MyStruct<MyStruct<MyStruct<_>>>`
    = note: required because of the requirements on the impl of `Methods2<MyStruct<MyStruct<MyStruct<MyStruct<_>>>>>` for `&'a MyStruct<MyStruct<MyStruct<MyStruct<_>>>>`
    = note: required because of the requirements on the impl of `for<'a> AllCombos<MyStruct<MyStruct<MyStruct<MyStruct<_>>>>>` for `&'a MyStruct<MyStruct<MyStruct<MyStruct<_>>>>`
    = note: required because of the requirements on the impl of `Methods2<MyStruct<MyStruct<MyStruct<MyStruct<MyStruct<_>>>>>>` for `&'a MyStruct<MyStruct<MyStruct<MyStruct<MyStruct<_>>>>>`
    = note: required because of the requirements on the impl of `for<'a> AllCombos<MyStruct<MyStruct<MyStruct<MyStruct<MyStruct<_>>>>>>` for `&'a MyStruct<MyStruct<MyStruct<MyStruct<MyStruct<_>>>>>`
note: required by `lift`
   --> src/main.rs:109:1
    |
109 | / fn lift<Float>(x: Float) -> MyStruct<Float>
110 | | where
111 | |     Float: AllCombos<Float>,
112 | |     for<'a> &'a Float: AllCombos<Float>,
113 | | {
114 | |     MyStruct { x }
115 | | }
    | |_^

Теперь это можно решить, заменив вызов на lift полным синтаксисом, который был закомментирован в вышеприведенном коде

let bar = lift::<f32>(4.0_f32).min(lift::<f32>(2.0_f32));

Хотя это работает, это больший код,это приводит к аннотациям везде, что обременительно. Я бы хотел, чтобы оригинальный вызов

let bar = lift(4.0_f32).min(lift(2.0_f32));

работал. Я могу сделать это, заменив реализацию признака AllCombos

impl<T, NonRef> AllCombos<NonRef> for T where
    T: Methods2<NonRef, Output = NonRef>
        + for<'a> Methods2<&'a NonRef, Output = NonRef>
{
}

явными реализациями для каждого типа

impl AllCombos<f32> for f32 {}
impl AllCombos<f32> for &f32 {}
impl AllCombos<MyStruct<f32>> for MyStruct<f32> {}
impl AllCombos<MyStruct<f32>> for &MyStruct<f32> {}

Затем все работает как положено. Однако это несколько неудобно, потому что оно должно быть указано для всех новых типов дважды. Существуют ли другие варианты, которые сохраняют общую реализацию AllCombos, но впоследствии не требуют полностью определенного синтаксиса для всех вызовов функций для методов в этой черте?

...