Rust 如何移动不可复制的堆栈变量?
How does Rust move stack variables that are not Copyable?
这里记录了 Rust 的移动语义的一个很好的例子:Rust Move Semantics 在 Rust By Example 网站上。
我对这两个案例的展示有了基本的了解。第一个是基元如何可以有一个新的别名,并且原始的仍然可以使用,因为最终结果是一个副本,因为 i32
利用了 Copy
特性。这对我来说很有意义。
此外,出于许多充分的原因,第二个示例在具有多个引用堆上 i32
的别名方面是有意义的。 Rust 强制执行所有权规则,因此在创建新绑定后不能使用原始别名。这有助于防止数据争用、双重释放等。
不过好像还有第三种情况没有讲到。 Rust 如何实现未实现 Copy
特征的堆栈分配结构的移动? 这用以下代码说明:
#[derive(Debug)]
struct Employee{
age: i32,
}
fn do_something(m: Employee){
println!("{:?}", m);
}
fn main() {
let x = Employee {
age: 25,
};
do_something(x);
//compiler error below because x has moved
do_something(x);
}
这我知道:在上面的例子中,Rust 将在堆栈 上分配Employee
。上面的结构 没有实现 Copy
特征 ,因此在分配给新别名时不会被复制。这让我很困惑,因为如果 Employee
结构在堆栈上分配并且也没有实现 Copy
特征 where/how 它会移动吗?它会实际移动到 do_something()
的堆栈框架吗?
如能帮助解释这个难题,我们将不胜感激。
Does it physically get moved to do_something()
's stack frame?
是的。非 Copy
类型的物理移动方式与 Copy
类型完全相同:具有 memcpy
。您已经了解原始 Copy
类型被复制到新位置(例如新堆栈框架)byte-by-byte.
现在考虑 Box
的实现:
struct Box<T> {
ptr: *const T,
}
当你有
let b = Box::new(27i32);
do_something(b); // `b` is moved into `do_something`
然后在堆上分配一个 i32
并且 Box
将原始指针保存到该堆分配的内存。注意直接Box
(里面的裸指针)是直接在栈上的,不是在堆上!只有 i32
在堆上。
当 Box
被移动时,它被 memcpy
ed,正如我刚才所说的。这意味着堆栈内容被复制 (!!)...因此只复制指针 byte-by-byte。 i32
没有第二个版本!
在身体移动方面,Copy
和非 Copy
类型之间没有区别。唯一的区别是编译器对这些类型执行不同的规则。
这里记录了 Rust 的移动语义的一个很好的例子:Rust Move Semantics 在 Rust By Example 网站上。
我对这两个案例的展示有了基本的了解。第一个是基元如何可以有一个新的别名,并且原始的仍然可以使用,因为最终结果是一个副本,因为 i32
利用了 Copy
特性。这对我来说很有意义。
此外,出于许多充分的原因,第二个示例在具有多个引用堆上 i32
的别名方面是有意义的。 Rust 强制执行所有权规则,因此在创建新绑定后不能使用原始别名。这有助于防止数据争用、双重释放等。
不过好像还有第三种情况没有讲到。 Rust 如何实现未实现 Copy
特征的堆栈分配结构的移动? 这用以下代码说明:
#[derive(Debug)]
struct Employee{
age: i32,
}
fn do_something(m: Employee){
println!("{:?}", m);
}
fn main() {
let x = Employee {
age: 25,
};
do_something(x);
//compiler error below because x has moved
do_something(x);
}
这我知道:在上面的例子中,Rust 将在堆栈 上分配Employee
。上面的结构 没有实现 Copy
特征 ,因此在分配给新别名时不会被复制。这让我很困惑,因为如果 Employee
结构在堆栈上分配并且也没有实现 Copy
特征 where/how 它会移动吗?它会实际移动到 do_something()
的堆栈框架吗?
如能帮助解释这个难题,我们将不胜感激。
Does it physically get moved to
do_something()
's stack frame?
是的。非 Copy
类型的物理移动方式与 Copy
类型完全相同:具有 memcpy
。您已经了解原始 Copy
类型被复制到新位置(例如新堆栈框架)byte-by-byte.
现在考虑 Box
的实现:
struct Box<T> {
ptr: *const T,
}
当你有
let b = Box::new(27i32);
do_something(b); // `b` is moved into `do_something`
然后在堆上分配一个 i32
并且 Box
将原始指针保存到该堆分配的内存。注意直接Box
(里面的裸指针)是直接在栈上的,不是在堆上!只有 i32
在堆上。
当 Box
被移动时,它被 memcpy
ed,正如我刚才所说的。这意味着堆栈内容被复制 (!!)...因此只复制指针 byte-by-byte。 i32
没有第二个版本!
在身体移动方面,Copy
和非 Copy
类型之间没有区别。唯一的区别是编译器对这些类型执行不同的规则。