Dan*_*iel 7 struct traits rust
当我定义这样的结构时,我可以通过值将它传递给函数而不添加任何特定的:
#[derive(Debug)]
struct MyType {
member: u16,
}
fn my_function(param: MyType) {
println!("param.member: {}", param.member);
}
Run Code Online (Sandbox Code Playgroud)
当我想创建一个MyType具有默认值的实例数组时
fn main() {
let array = [MyType { member: 1234 }; 100];
println!("array[42].member: ", array[42].member);
}
Run Code Online (Sandbox Code Playgroud)
Rust编译器告诉我:
error[E0277]: the trait bound `MyType: std::marker::Copy` is not satisfied
--> src/main.rs:11:17
|
11 | let array = [MyType { member: 1234 }; 100];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `MyType`
|
= note: the `Copy` trait is required because the repeated element will be copied
Run Code Online (Sandbox Code Playgroud)
当我执行Copy和Clone一切工作:
impl Copy for MyType {}
impl Clone for MyType {
fn clone(&self) -> Self {
MyType {
member: self.member.clone(),
}
}
}
Run Code Online (Sandbox Code Playgroud)
为什么我需要指定一个空的Copy特征实现?
有没有更简单的方法来做到这一点,还是我必须重新思考一些事情?
为什么MyType按值传递函数的实例时它有效?我的猜测是它被移动了,所以首先没有副本.
Vla*_*eev 20
与C/C++相反,Rust在复制和移动的类型之间有非常明确的区别.请注意,这只是语义上的区别; 在实现级别移动是一个浅的逐字节副本,但是,编译器对您可以对移动的变量执行的操作设置了某些限制.
默认情况下,每种类型只能移动(不可复制).这意味着这些类型的值会被移动:
let x = SomeNonCopyableType::new();
let y = x;
x.do_something(); // error!
do_something_else(x); // error!
Run Code Online (Sandbox Code Playgroud)
您看,存储的值x已移至y,因此您无法执行任何操作x.
移动语义是Rust中所有权概念的一个非常重要的部分.您可以在官方指南中阅读更多相关内容.
但是,有些类型很简单,因此它们的字节副本也是它们的语义副本:如果逐字节复制一个值,您将获得一个新的完全独立的值.例如,原始数字就是这种类型.这样的属性由CopyRust中的trait 指定,即如果类型实现Copy,则此类型的值可隐式复制.Copy不包含方法; 它的存在仅仅是为了表明实现类型具有某些属性,因此通常称为标记特征(以及执行类似事务的其他特征).
但是,它并不适用于所有类型.例如,像动态分配的向量这样的结构不能自动复制:如果是,则包含在其中的分配的地址也将被字节复制,然后这种向量的析构函数将在同一分配上运行两次,从而导致这种情况指针被释放两次,这是一个内存错误.
因此,默认情况下,Rust中的自定义类型不可复制.但你可以选择使用#[derive(Copy, Clone)](或者,正如你所注意到的那样,使用直接impl;它们是等效的,但derive通常读得更好):
#[derive(Copy, Clone)]
struct MyType {
member: u16
}
Run Code Online (Sandbox Code Playgroud)
(导出Clone是必要的,因为Copy继承Clone,所以一切都Copy必须是Clone)
如果您的类型原则上可以自动复制,也就是说,它没有关联的析构函数及其所有成员Copy,那么derive您的类型也将是Copy.
您可以Copy精确地使用数组初始值设定项中的类型,因为数组将使用此初始化程序中使用的值的字节副本进行初始化,因此您必须实现类型Copy以指定它确实可以自动复制.
以上是1和2的答案.至于3,是的,你是绝对正确的.它确实正常工作,因为值被移入函数中.如果MyType在将函数传递给函数后尝试使用类型变量,则会很快发现有关使用移动值的错误.
为什么我需要指定一个空的Copy trait实现?
Copy是一种特殊的内置特性,T实现Copy表示T使用浅字节副本复制类型的值是安全的.
这个简单的定义意味着只需要告诉编译器这些语义是正确的,因为运行时行为没有根本改变:移动(非Copy类型)和"副本"都是浅字节副本,它只是一个以后是否可以使用该来源的问题.有关详细信息,请参阅旧答案.
(如果内容MyType不是Copy自身,编译器会抱怨;之前它会自动实现,但所有内容都会因选择加入内置特征而改变.)
创建一个数组是通过浅拷贝复制值,如果T是这样,这保证是安全的Copy.它是在更一般的情况下安全,#5244涵盖了其中的一部分,但在核心,非Copy结构将不能够被用来自动创建一个固定长度的数组,因为编译器不能告诉复制安全/正确.
有没有更简单的方法来做到这一点,还是我必须重新思考一些事情(我来自C)?
#[derive(Copy)]
struct MyType {
member: u16
}
Run Code Online (Sandbox Code Playgroud)
将插入适当的空实现(#[derive]与其他几个特征一起使用,例如经常看到的特征)#[derive(Copy, Clone, PartialEq, Eq)].
为什么
MyType按值传递函数的实例时它有效?我的猜测是它被移动了,所以首先没有副本.
好吧,如果没有调用函数,就不会看到移动与复制行为(如果你将它调用两次相同的非Copy值,则编译器会发出有关移动值的错误).但是,"移动"和"复制"在机器上基本相同.值的所有按值使用都是Rust中的浅层副本,就像在C中一样.