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
}