Как мне обеспечить реализацию универсальной структуры в Rust? - PullRequest
0 голосов
/ 03 февраля 2019

У меня есть структура MyStruct, которая принимает общий параметр T: SomeTrait, и я хочу реализовать метод new для MyStruct.Это работает:

/// Constraint for the type parameter `T` in MyStruct
pub trait SomeTrait: Clone {}

/// The struct that I want to construct with `new`
pub struct MyStruct<T: SomeTrait> {
    value: T,
}

fn new<T: SomeTrait>(t: T) -> MyStruct<T> {
    MyStruct { value: t }
}

fn main() {}

Я хотел поместить функцию new в блок impl следующим образом:

impl MyStruct {
    fn new<T: SomeTrait>(t: T) -> MyStruct<T> {
        MyStruct { value: t }
    }
}

Но это не скомпилируется с:

error[E0107]: wrong number of type arguments: expected 1, found 0
 --> src/main.rs:9:6
  |
9 | impl MyStruct {
  |      ^^^^^^^^ expected 1 type argument

Если я попытаюсь выразить это следующим образом:

impl MyStruct<T> {
    fn new(t: T) -> MyStruct<T> {
        MyStruct { value: t }
    }
}

Ошибка изменится на:

error[E0412]: cannot find type `T` in this scope
 --> src/main.rs:9:15
  |
9 | impl MyStruct<T> {
  |               ^ not found in this scope

Как обеспечить реализацию универсальной структуры?Где я могу разместить общие параметры и их ограничения?

1 Ответ

0 голосов
/ 03 февраля 2019

Параметр типа <T: SomeTrait> должен идти сразу после ключевого слова impl:

impl<T: SomeTrait> MyStruct<T> {
    fn new(t: T) -> Self {
        MyStruct { value: t }
    }
}

Если список типов и ограничений в impl<...> становится слишком длинным, вы можете использовать where-синтаксис и перечислить ограничения отдельно:

impl<T> MyStruct<T>
where
    T: SomeTrait,
{
    fn new(t: T) -> Self {
        MyStruct { value: t }
    }
}

Обратите внимание на использование Self, которое является ярлыком для MyStruct<T>, доступного внутри блока impl.


Примечания

  1. Причина, по которой требуется impl<T>, объясняется в в этом ответе .По сути, это сводится к тому, что оба impl<T> MyStruct<T> и impl MyStruct<T> являются действительными, но означают разные вещи.

  2. Когда вы перемещаете new в блок impl,вы должны удалить лишние параметры типа, иначе интерфейс вашей структуры станет непригодным для использования, как показано в следующем примере:

    // trait SomeTrait and struct MyStruct as above
    // [...]
    
    impl<T> MyStruct<T>
    where
        T: SomeTrait,
    {
        fn new<S: SomeTrait>(t: S) -> MyStruct<S> {
            MyStruct { value: t }
        }
    }
    
    impl SomeTrait for u64 {}
    impl SomeTrait for u128 {}
    
    fn main() {
        // just a demo of problematic code, don't do this!
        let a: MyStruct<u128> = MyStruct::<u64>::new::<u128>(1234);
        //                                 ^
        //                                 |
        //        This is an irrelevant type
        //        that cannot be inferred. Not only will the compiler
        //        force you to provide an irrelevant type, it will also
        //        not prevent you from passing incoherent junk as type
        //        argument, as this example demonstrates. This happens 
        //        because `S` and `T` are completely unrelated.
    }
    
...