【问题标题】:What does the equal '=' actually do in rust (with/without 'let')?相等的'='实际上在rust中做了什么(有/没有'let')?
【发布时间】:2021-04-08 08:52:04
【问题描述】:

当 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的内存转移到b执行move操作,a已经失效了?似乎在使用 let 关键字时进行了浅拷贝(如 C 中的 memcpy),而没有调用 drop 函数。

  2. ctmp的地址相同。这时候好像是执行了真正的move,而不是memcpy,没有调用drop函数。

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

let 是否避免挂断电话?

【问题讨论】:

  • 您不使用--release 标志来启用优化。未优化代码的行为方式几乎与源代码相同

标签: rust binding compiler-construction assign


【解决方案1】:

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

长答案

  1. 无论何时在代码中指定变量,编译器都必须分配内存以在运行时存储变量的值。 因此,对于abc 中的每一个,编译器都必须分配一个内存位置。 这就是ab 具有不同地址的原因。 let mut b = a 从内存位置 a 移动到内存位置 b。 移动后,a 将无法再使用。
  2. ctmp 不是同一个函数的一部分。 此外,在任何时候,ctmp 中只有一个可以在范围内,因此它们可以重复使用相同的内存位置。
  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() 中分配给它的值,必须先删除它。

如需进一步解释,您可以查看 let statementsassignment expressions 的 Rust 参考。 Drop trait 的文档也可能很有趣。

【讨论】:

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

    【讨论】:

      猜你喜欢
      • 2019-07-02
      • 2023-02-23
      • 2013-06-02
      • 2020-11-21
      • 2015-02-26
      • 2017-06-29
      • 2013-06-13
      • 2021-10-25
      • 2011-08-12
      相关资源
      最近更新 更多