rust (with/without 'let') 中等号 '=' 的实际作用是什么?
What does the equal '=' actually do in rust (with/without 'let')?
当 rust 执行 memcpy 并调用 drop 函数时,我感到很困惑。我看了一些相关的页面,但没有找到详细的描述。
这里有一些简单的代码:
struct MyType {
name: String,
age: i32
}
impl MyType {
fn new() -> Self {
let tmp = MyType {
name: String::from("Joy"),
age: 1
};
let addr = &tmp as *const MyType as usize;
println!("Calling new.");
println!("tmp : name: {}, age: {}", tmp.name, tmp.age);
println!("addr: 0x{:X}\n",addr);
tmp
}
}
impl Drop for MyType {
fn drop(&mut self) {
println!("Calling drop.\n");
}
}
fn main() {
println!("");
let a = MyType{
name: String::from("Tom"),
age : 10
};
let addr = &a as *const MyType as usize;
println!(" a : name: {}, age: {}", a.name, a.age);
println!("addr: 0x{:X}\n",addr);
let mut b = a;
let addr = &b as *const MyType as usize;
println!(" b : name: {}, age: {}", b.name, b.age);
println!("addr: 0x{:X}\n",addr);
b = MyType::new();
let addr = &b as *const MyType as usize;
println!(" b : name: {}, age: {}", b.name, b.age);
println!("addr: 0x{:X}\n",addr);
let c = MyType::new();
let addr = &c as *const MyType as usize;
println!(" c : name: {}, age: {}", c.name, c.age);
println!("addr: 0x{:X}\n",addr);
b = c;
let addr = &b as *const MyType as usize;
println!(" b : name: {}, age: {}", b.name, b.age);
println!("addr: 0x{:X}\n",addr);
}
并输出:
> Executing task: cargo run --package hello_world --bin hello_world <
Compiling hello_world v0.1.0 (/home/dji/proj_learn_rust/hello_world)
Finished dev [unoptimized + debuginfo] target(s) in 0.21s
Running `target/debug/hello_world`
a : name: Tom, age: 10
addr: 0x7FFDB8636AF0
b : name: Tom, age: 10
addr: 0x7FFDB8636BE0
Calling new.
tmp : name: Joy, age: 1
addr: 0x7FFDB8636CB8
Calling drop.
b : name: Joy, age: 1
addr: 0x7FFDB8636BE0
Calling new.
tmp : name: Joy, age: 1
addr: 0x7FFDB8636DB0
c : name: Joy, age: 1
addr: 0x7FFDB8636DB0
Calling drop.
b : name: Joy, age: 1
addr: 0x7FFDB8636BE0
Calling drop.
let mut b = a;
之后,好像a
和b
的地址不一样。为什么在执行move操作时,不直接将a
的内存转移到b
,因为a
已经无效了?似乎在使用 let
关键字时进行了浅拷贝(如 C 中的 memcpy),而没有调用 drop 函数。
c
和tmp
地址相同。这时候好像真正的move被执行了,而不是memcpy,没有调用drop函数
但为什么 b = c;
会调用 drop 函数而 let mut b = a;
和 let c = MyType::new();
不会?
let
是否避免掉线?
- 每次调用函数时,它都会在堆栈上分配新的帧。当函数退出时,它会掉帧。这就是为什么
a
和 tmp
有不同的地址。如果您使用 --release
标志进行编译,可能会发生变化,因为编译器可以内联函数调用或执行许多其他优化。
- 您没有调用
new
函数,所以一切都发生在同一个堆栈帧中。这就是为什么编译器可能只是重新分配变量而没有对内存进行任何实际操作。
- 两个示例之间的区别在于,在第二个示例中,您的
b
已经拥有一个值(之前在行 b = MyType::new();
上创建)。所以这个值应该在从 c
分配后被删除。换句话说,它不会删除 b
的值,而是实际上之前使用 b = MyType::new();
. 行创建的值
先简单回答一下:let
不是避免drop calling,而是用来引入新的变量。
=
没有 let
是简单的变量赋值。
长答案
- 无论何时在代码中指定变量,编译器都必须在运行时分配内存来存储变量的值。
因此,对于
a
、b
和 c
中的每一个,编译器都必须分配一个内存位置。
这就是 a
和 b
具有不同地址的原因。
let mut b = a
将 值 从内存位置 a
移动到内存位置 b
。
在那次移动之后,a
不能再使用了。
c
和 tmp
不是同一函数的一部分。
此外,任何时候只有 c
和 tmp
之一可以在范围内,因此它们可以重用相同的内存位置。
- 在
let mut b = a
中,b
还没有值。
因此,drop
也没什么。
然而,在 b = MyType::new()
中,b
已经包含了在 let mut b = a
中分配给它的值。
然后删除该值。
您可以通过更改 MyType
的 Drop
实现来打印删除值的名称来验证这一点。
同样,c
还不包含 let c = MyType::new()
中的值。
然而,在 b = c
中,b
已经包含在 b = MyType::new()
中分配给它的值,必须首先删除它。
如需进一步说明,您可以查看 let statements and assignment expressions 的 Rust 参考资料。
Drop
trait 的文档也很有趣。
当 rust 执行 memcpy 并调用 drop 函数时,我感到很困惑。我看了一些相关的页面,但没有找到详细的描述。
这里有一些简单的代码:
struct MyType {
name: String,
age: i32
}
impl MyType {
fn new() -> Self {
let tmp = MyType {
name: String::from("Joy"),
age: 1
};
let addr = &tmp as *const MyType as usize;
println!("Calling new.");
println!("tmp : name: {}, age: {}", tmp.name, tmp.age);
println!("addr: 0x{:X}\n",addr);
tmp
}
}
impl Drop for MyType {
fn drop(&mut self) {
println!("Calling drop.\n");
}
}
fn main() {
println!("");
let a = MyType{
name: String::from("Tom"),
age : 10
};
let addr = &a as *const MyType as usize;
println!(" a : name: {}, age: {}", a.name, a.age);
println!("addr: 0x{:X}\n",addr);
let mut b = a;
let addr = &b as *const MyType as usize;
println!(" b : name: {}, age: {}", b.name, b.age);
println!("addr: 0x{:X}\n",addr);
b = MyType::new();
let addr = &b as *const MyType as usize;
println!(" b : name: {}, age: {}", b.name, b.age);
println!("addr: 0x{:X}\n",addr);
let c = MyType::new();
let addr = &c as *const MyType as usize;
println!(" c : name: {}, age: {}", c.name, c.age);
println!("addr: 0x{:X}\n",addr);
b = c;
let addr = &b as *const MyType as usize;
println!(" b : name: {}, age: {}", b.name, b.age);
println!("addr: 0x{:X}\n",addr);
}
并输出:
> Executing task: cargo run --package hello_world --bin hello_world <
Compiling hello_world v0.1.0 (/home/dji/proj_learn_rust/hello_world)
Finished dev [unoptimized + debuginfo] target(s) in 0.21s
Running `target/debug/hello_world`
a : name: Tom, age: 10
addr: 0x7FFDB8636AF0
b : name: Tom, age: 10
addr: 0x7FFDB8636BE0
Calling new.
tmp : name: Joy, age: 1
addr: 0x7FFDB8636CB8
Calling drop.
b : name: Joy, age: 1
addr: 0x7FFDB8636BE0
Calling new.
tmp : name: Joy, age: 1
addr: 0x7FFDB8636DB0
c : name: Joy, age: 1
addr: 0x7FFDB8636DB0
Calling drop.
b : name: Joy, age: 1
addr: 0x7FFDB8636BE0
Calling drop.
let mut b = a;
之后,好像a
和b
的地址不一样。为什么在执行move操作时,不直接将a
的内存转移到b
,因为a
已经无效了?似乎在使用let
关键字时进行了浅拷贝(如 C 中的 memcpy),而没有调用 drop 函数。c
和tmp
地址相同。这时候好像真正的move被执行了,而不是memcpy,没有调用drop函数但为什么
b = c;
会调用 drop 函数而let mut b = a;
和let c = MyType::new();
不会?
let
是否避免掉线?
- 每次调用函数时,它都会在堆栈上分配新的帧。当函数退出时,它会掉帧。这就是为什么
a
和tmp
有不同的地址。如果您使用--release
标志进行编译,可能会发生变化,因为编译器可以内联函数调用或执行许多其他优化。 - 您没有调用
new
函数,所以一切都发生在同一个堆栈帧中。这就是为什么编译器可能只是重新分配变量而没有对内存进行任何实际操作。 - 两个示例之间的区别在于,在第二个示例中,您的
b
已经拥有一个值(之前在行b = MyType::new();
上创建)。所以这个值应该在从c
分配后被删除。换句话说,它不会删除b
的值,而是实际上之前使用b = MyType::new();
. 行创建的值
先简单回答一下:let
不是避免drop calling,而是用来引入新的变量。
=
没有 let
是简单的变量赋值。
长答案
- 无论何时在代码中指定变量,编译器都必须在运行时分配内存来存储变量的值。
因此,对于
a
、b
和c
中的每一个,编译器都必须分配一个内存位置。 这就是a
和b
具有不同地址的原因。let mut b = a
将 值 从内存位置a
移动到内存位置b
。 在那次移动之后,a
不能再使用了。 c
和tmp
不是同一函数的一部分。 此外,任何时候只有c
和tmp
之一可以在范围内,因此它们可以重用相同的内存位置。- 在
let mut b = a
中,b
还没有值。 因此,drop
也没什么。 然而,在b = MyType::new()
中,b
已经包含了在let mut b = a
中分配给它的值。 然后删除该值。 您可以通过更改MyType
的Drop
实现来打印删除值的名称来验证这一点。 同样,c
还不包含let c = MyType::new()
中的值。 然而,在b = c
中,b
已经包含在b = MyType::new()
中分配给它的值,必须首先删除它。
如需进一步说明,您可以查看 let statements and assignment expressions 的 Rust 参考资料。
Drop
trait 的文档也很有趣。