繁体   English   中英

rust(有/无'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.
  1. 之后let mut b = a; ,好像ab的地址不一样。 既然a不再有效,为什么不直接a的memory传给b呢? 似乎在使用let关键字时进行了浅拷贝(如 C 中的 memcpy),而没有调用 drop function。

  2. ctmp的地址是一样的。 这时候,似乎执行了真正的move而不是memcpy,没有调用drop function。

  3. 但是为什么b = c; 调用 drop function 而let mut b = a; let c = MyType::new(); 不要?

let避免掉线吗?

让我们从简短的回答开始: let不是避免 drop 调用,它是用来引入新变量的。 =没有let是简单的变量赋值。

长答案

  1. 每当您在代码中指定变量时,编译器必须分配 memory 以在运行时存储变量的值。 因此,对于abc中的每一个,编译器必须分配一个 memory 位置。 这就是ab具有不同地址的原因。 let mut b = a从 memory 位置a移动到 memory 位置b 在那之后, a就不能再使用了。
  2. ctmp不是同一个 function 的一部分。 此外, ctmp中只有一个可以随时在 scope 中,因此它们可以重复使用相同的 memory 位置。
  3. let mut b = a中, b还没有值。 因此,没有什么可drop的。 但是,在b = MyType::new()中, b已经包含在let mut b = a中分配给它的值。 然后删除该值。 您可以通过更改MyTypeDrop实现来打印已删除值的名称来验证这一点。 同样, c尚不包含let c = MyType::new()中的值。 但是,在b = c中, b已经包含在b = MyType::new()中分配给它的值,必须首先删除它。

有关更多解释,您可以查看 Rust 参考中的let 语句赋值表达式 Drop trait的文档也可能很有趣。

  1. 每次调用 function 时,它都会在堆栈上分配新的帧。 当 function 退出时,它的帧丢失。 这就是为什么你有不同的地址atmp 可能,如果您将使用可能更改的--release标志进行编译,因为编译器可以内联 function 调用或执行许多其他优化。
  2. 你没有调用new function,所以一切都发生在同一个堆栈帧中。 这就是为什么编译器可能只是重新分配变量而无需使用 memory 进行任何实际操作。
  3. 两个示例之间的区别在于,在第二个示例中,您的b已经拥有一个值(之前在b = MyType::new();行上创建)。 所以这个值应该在从c分配后删除。 换句话说,它删除的不是b的值,而是实际上之前使用行b = MyType::new();创建的。 .

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM