如何在Rust中提供通用结构的实现?

27

我有一个结构体MyStruct,它带有一个泛型参数T: SomeTrait,我想为MyStruct实现一个new方法。这个可以工作:

/// 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个回答

33
类型参数<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,它是在impl块中可用的MyStruct<T>的快捷方式。


备注

  1. The reason why impl<T> is required is explained in this answer. Essentially, it boils down to the fact that both impl<T> MyStruct<T> and impl MyStruct<T> are valid, but mean different things.

  2. When you move new into the impl block, you should remove the superfluous type parameters, otherwise the interface of your struct will become unusable, as the following example shows:

    // 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.
    }
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接