Struct 为什么默认(结构值)数组初始化需要复制特性?

Struct 为什么默认(结构值)数组初始化需要复制特性?,struct,rust,traits,Struct,Rust,Traits,当我定义这样的结构时,我可以通过值将其传递给函数,而无需添加任何特定内容: #[derive(Debug)] struct MyType { member: u16, } fn my_function(param: MyType) { println!("param.member: {}", param.member); } 当我想创建一个带有默认值的MyType实例数组时 fn main() { let array = [MyType { member: 1234

当我定义这样的结构时,我可以通过值将其传递给函数,而无需添加任何特定内容:

#[derive(Debug)]
struct MyType {
    member: u16,
}

fn my_function(param: MyType) {
    println!("param.member: {}", param.member);
}
当我想创建一个带有默认值的
MyType
实例数组时

fn main() {
    let array = [MyType { member: 1234 }; 100];
    println!("array[42].member: ", array[42].member);
}
Rust编译器告诉我:

error[E0277]:不满足特性绑定'MyType:std::marker::Copy'
-->src/main.rs:11:17
|
11 | let数组=[MyType{member:1234};100];
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^未为“MyType”实现特征“std::marker::Copy”`
|
=注意:`Copy`特性是必需的,因为重复的元素将被复制
当我实施
复制
克隆
时,一切都正常:

impl Copy for MyType {}
impl Clone for MyType {
    fn clone(&self) -> Self {
        MyType {
            member: self.member.clone(),
        }
    }
}
  • 为什么我需要指定一个空的
    Copy
    trait实现

  • 有没有更简单的方法来做这件事,还是我必须重新思考

  • 当通过值将
    MyType
    的实例传递给函数时,它为什么工作?我猜它正在被移动,所以一开始就没有拷贝


  • 与C/C++相反,Rust在复制和移动的类型之间有非常明确的区别。注意,这只是语义上的区别;在实现级别上,move是一个浅字节复制,但是,编译器对您可以对从中移动的变量执行的操作设置了某些限制

    默认情况下,每个类型仅可移动(不可复制)。这意味着这些类型的值会四处移动:

    let x = SomeNonCopyableType::new();
    let y = x;
    x.do_something();      // error!
    do_something_else(x);  // error!
    
    您可以看到,存储在
    x
    中的值已移动到
    y
    ,因此您不能对
    x
    执行任何操作

    移动语义是Rust中所有权概念的重要组成部分。你可以阅读更多关于它的内容

    然而,有些类型足够简单,所以它们的字节复制也是它们的语义复制:如果你逐字节复制一个值,你将得到一个新的完全独立的值。例如,基本数字就是这样的类型。此类属性在Rust中由
    Copy
    trait指定,即,如果类型实现了
    Copy
    ,则此类型的值可隐式复制<代码>复制不包含方法;它的存在仅仅是为了标记实现类型具有某些属性,因此它通常被称为标记特征(以及其他一些做类似事情的特征)

    但是,它并不适用于所有类型。例如,像动态分配的向量这样的结构不能自动复制:如果是,则其中包含的分配地址也将被字节复制,然后此类向量的析构函数将在同一分配上运行两次,导致此指针被释放两次,这是内存错误

    因此,默认情况下,Rust中的自定义类型是不可复制的。但您可以使用
    #[派生(复制、克隆)]
    (或者,正如您所注意到的,使用直接
    impl
    ;它们是等效的,但是
    派生
    通常读起来更好):

    (派生
    克隆
    是必要的,因为
    复制
    继承
    克隆
    ,所以
    复制
    的所有内容也必须是
    克隆

    如果您的类型原则上可以自动复制,也就是说,它没有关联的析构函数,并且它的所有成员都是
    Copy
    ,那么使用
    derivate
    您的类型也将是
    Copy

    您可以在数组初始值设定项中使用
    Copy
    类型,因为数组将使用此初始值设定项中使用的值的字节副本进行初始化,因此您的类型必须实现
    Copy
    以指定它确实可以自动复制

    以上是对1和2的回答。至于第三点,是的,你是绝对正确的。它确实可以工作,因为值被移动到函数中。如果在将
    MyType
    type变量传递到函数后尝试使用该变量,则很快就会注意到使用移动值时出现错误

    为什么我需要指定一个空拷贝特性实现

    Copy
    是一种特殊的内置特性,
    T
    实现
    Copy
    表示使用浅字节拷贝复制
    T
    类型的值是安全的

    这个简单的定义意味着只需要告诉编译器这些语义是正确的,因为运行时行为没有根本性的改变:move(非
    拷贝
    类型)和“Copy”都是浅字节拷贝,只是源代码以后是否可用的问题。看

    (如果
    MyType
    的内容不是
    Copy
    本身,编译器会投诉;以前它会自动实现,但所有内容都会更改。)

    创建数组就是通过浅拷贝复制值,如果
    T
    Copy
    ,则可以保证安全。它在更一般的情况下是安全的,涵盖了其中的一些情况,但在核心部分,非复制结构将无法用于自动创建固定长度数组,因为编译器无法判断复制是否安全/正确

    有没有更简单的方法来做这件事,或者我必须重新思考一些事情(我来自C)

    将插入适当的空实现(
    #[deriver]
    与其他几个特性一起工作,例如,人们经常看到
    #[deriver(Copy、Clone、PartialEq、Eq)]

    当通过值将
    MyType
    的实例传递给函数时,它为什么工作?我猜它正在被移动,所以一开始就没有拷贝

    如果不调用函数,就看不到move vs.copy行为(如果对同一个非
    copy
    值调用两次,编译器将发出一个关于移动值的错误)。但是,机器上的“移动”和“复制”本质上是相同的。在R中,值的所有按值使用在语义上都是浅拷贝
    #[derive(Copy, Clone)]
    struct MyType {
        member: u16
    }
    
    #[derive(Copy)]
    struct MyType {
        member: u16
    }