【问题标题】:`#[derive(Clone)]` seems to wrongfully enforce generic to be `clone``#[derive(Clone)]` 似乎错误地将泛型强制为`clone`
【发布时间】:2022-10-26 03:44:15
【问题描述】:

似乎在派生 Clone 时,Rust 将 Clone 特征要求转发给不需要该特征的泛型,就像它们被包裹在 Arc 中一样。

我是否误解了 Clone 的工作原理还是编译器错误?

考虑以下代码,其中a.clone() 有效,但b.clone() 无效。 另请注意,没有b.clone() 调用,代码编译良好,表明#[derive(Clone)] 有效。

use std::sync::Arc;

struct Unclonable {}

struct A<T>(Arc<T>);
impl<T> Clone for A<T> {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

#[derive(Clone)]
struct B<T>(Arc<T>);

fn main() {
    let a = A(Arc::new(Unclonable {}));
    let b = B(Arc::new(Unclonable {}));

    // Works
    a.clone();
    // Fails
    b.clone();
}
   |
3  | struct Unclonable {}
   | ----------------- doesn't satisfy `Unclonable: Clone`
...
13 | struct B<T>(Arc<T>);
   | --------------------
   | |
   | method `clone` not found for this
   | doesn't satisfy `B<Unclonable>: Clone`
...
22 |     b.clone();
   |       ^^^^^ method cannot be called on `B<Unclonable>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `Unclonable: Clone`
           which is required by `B<Unclonable>: Clone`
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `clone`, perhaps you need to implement it:
           candidate #1: `Clone`
help: consider annotating `Unclonable` with `#[derive(Clone)]`
   |
3  | #[derive(Clone)]
   |

当我expand 宏时,我看到以下生成的代码:

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2018::*;
#[macro_use]
extern crate std;
use std::sync::Arc;
struct Unclonable {}
struct A<T>(Arc<T>);
impl<T> Clone for A<T> {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}
struct B<T>(Arc<T>);
#[automatically_derived]
#[allow(unused_qualifications)]
impl<T: ::core::clone::Clone> ::core::clone::Clone for B<T> {
    #[inline]
    fn clone(&self) -> B<T> {
        match *self {
            B(ref __self_0_0) => B(::core::clone::Clone::clone(&(*__self_0_0))),
        }
    }
}
fn main() {
    let a = A(Arc::new(Unclonable {}));
    let b = B(Arc::new(Unclonable {}));
    a.clone();
    b.clone();
}

到底是怎么回事? 为什么 rust 编译器会添加&lt;T: ::core::clone::Clone&gt;??

或者这只是其中一种预期方式是手动实现Clone 的情况?

【问题讨论】:

    标签: generics rust


    【解决方案1】:

    这不是编译器错误,因为它是documented behavior,尽管人们可能会觉得它很奇怪。编译器会在派生时自动向泛型类型添加约束,即使实现中实际上不需要这些约束。例如,为B&lt;T&gt; 派生Clone 将仅实现Clone for B&lt;T&gt; 其中T: Clone,即使无论T 是否为Clone 都可能实现它。

    所以,就目前而言(也许编译器将来会对这些情况变得更聪明),是的,这是你必须手动实现的情况之一。

    【讨论】:

      【解决方案2】:

      如果您不介意 smallo 依赖,则可以避免苦差事1使用derivative crate 的手动实现:

      #[derive(Derivative)]
      #[derivative(Clone(bound = ""))]
      struct B<T>(Arc<T>);
      

      Playground

      1在小型结构的情况下,苦差事并没有那么糟糕,但是在具有许多变体的enum 的情况下,实现变得相当大。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-25
        • 2015-06-24
        • 2016-04-05
        • 2015-09-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多