【问题标题】:Deriving a trait results in unexpected compiler error, but the manual implementation works派生特征会导致意外的编译器错误,但手动实现有效
【发布时间】:2017-01-17 19:26:13
【问题描述】:

此代码(playground):

#[derive(Clone)]
struct Foo<'a, T: 'a> {
    t: &'a T,
}

fn bar<'a, T>(foo: Foo<'a, T>) {
    foo.clone();
}

... 无法编译:

error[E0599]: no method named `clone` found for struct `Foo<'a, T>` in the current scope
   --> src/main.rs:16:9
    |
3   | struct Foo<'a, T: 'a> {
    | ---------------------
    | |
    | method `clone` not found for this
    | doesn't satisfy `Foo<'_, T>: std::clone::Clone`
...
16  |     foo.clone();
    |         ^^^^^ method not found in `Foo<'a, T>`
    |
    = note: the method `clone` exists but the following trait bounds were not satisfied:
            `T: std::clone::Clone`
            which is required by `Foo<'_, T>: std::clone::Clone`
help: consider restricting the type parameter to satisfy the trait bound
    |
3   | struct Foo<'a, T: 'a> where T: std::clone::Clone {
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^

添加use std::clone::Clone; 不会改变任何东西,因为它已经在前奏中了。

当我删除 #[derive(Clone)] 并为 Foo 手动实现 Clone 时,它按预期编译

impl<'a, T> Clone for Foo<'a, T> {
    fn clone(&self) -> Self {
        Foo {
            t: self.t,
        }
    }
}

这是怎么回事?

  • #[derive()]-impls 和手动的有区别吗?
  • 这是编译器错误吗?
  • 还有什么我没想到的?

【问题讨论】:

    标签: rust clone


    【解决方案1】:

    答案隐藏在错误信息中:

        = note: the method `clone` exists but the following trait bounds were not satisfied:
                `T: std::clone::Clone`
                which is required by `Foo<'_, T>: std::clone::Clone`
    

    当您派生 Clone(以及许多其他自动派生的类型)时,它会在 all 泛型类型上添加 Clone 绑定。使用rustc -Z unstable-options --pretty=expanded,我们可以看到它变成了什么:

    impl <'a, T: ::std::clone::Clone + 'a> ::std::clone::Clone for Foo<'a, T> {
        #[inline]
        fn clone(&self) -> Foo<'a, T> {
            match *self {
                Foo { t: ref __self_0_0 } =>
                Foo{t: ::std::clone::Clone::clone(&(*__self_0_0)),},
            }
        }
    }
    

    this 的情况下,不需要绑定,因为泛型类型在引用后面。

    现在,您需要自己实现CloneThere's a Rust issue for this,但这是一个相对罕见的情况,有解决方法。

    【讨论】:

    【解决方案2】:

    如果您明确标记T 应该实现Clone,您的示例将毫无问题地派生Clone,如下所示:

    #[derive(Clone)]
    struct Foo<'a, T: 'a> {
        t: &'a T,
    }
    
    fn bar<'a, T: Clone>(foo: Foo<'a, T>) {
        foo.clone();
    }
    

    (Playground link)

    您可以避免显式指定边界似乎不寻常,但 Shepmaster 的回答似乎暗示编译器会隐式插入它,所以我的建议在功能上是相同的。

    【讨论】:

    • 这是一个很好的解决方案,但我不同意 功能相同 - 在这种情况下,结构只能包含实现 CloneT,但那个特定的bound 不是克隆引用的必需。如果程序出于其他原因需要克隆T,则此方法有效。
    • 哦,我假设您的示例中的T: ::std::clone::Clone + 'a 意味着T 无论如何都实现了Clone;我误会了吗?
    • @Shepmaster,经过一些测试,我发现您可以毫无问题地删除结构中T 上的Clone 绑定,并且通过这样做,我从扩展后的输出中产生完全相同的输出漂亮的打印机作为手册impl.
    • 不,这是我不清楚的错。我的意思是我的“解决方案”是在不派生的情况下实现Clone。这使得结构完全没有限制。
    猜你喜欢
    • 1970-01-01
    • 2016-04-15
    • 2011-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多