Rust是否释放了被覆盖变量的记忆?

时间:2018-01-12 13:32:44

标签: memory-management rust

我在Rust书中看到,您可以定义两个具有相同名称的不同变量:

let hello = "Hello";
let hello = "Goodbye";

println!("My variable hello contains: {}", hello);

打印出来:

My variable hello contains: Goodbye

第一次问候怎么了?它被释放了吗?我怎么能访问它?

我知道将两个变量命名为相同是不好的,但是如果这种情况偶然发生,因为我将其声明为100行,这可能是一个真正的痛苦。

2 个答案:

答案 0 :(得分:12)

Rust does not have a garbage collector

  

Rust是否释放了被覆盖变量的内存?

是的,否则会造成内存泄漏,这将是一个非常糟糕的设计决定。

  

第一个问候

会发生什么

shadowed

没什么"特别"发生在变量引用的数据上,而不是您无法再访问它。当变量超出范围时,它仍然被丢弃。

这里的代码在每个变量被删除时打印出来:

struct Noisy;
impl Drop for Noisy {
    fn drop(&mut self) {
        println!("Dropped")
    }
}

fn main() {
    println!("0");
    let thing = Noisy;
    println!("1");
    let thing = Noisy;
    println!("2");
}
0
1
2
Dropped
Dropped
  

我知道将两个变量命名为相同

是不好的

它不是"糟糕"它是一个设计决定。我会说像这样使用阴影是一个坏主意:

let x = "Anna";
println!("User's name is {}", x);
let x = 42;
println!("The tax rate is {}", x);

使用像这样的阴影对我来说是合理的:

let name = String::from("  Vivian ");
let name = name.trim();
println!("User's name is {}", x);

另见:

  

但是如果这种情况偶然发生,因为我宣布它下面100行,那可能是一个真正的痛苦。

不要有太大的功能,以至于你不小心"做一点事。这适用于任何编程语言。

  

有没有办法手动清理记忆?

您可以致电drop

println!("0");
let thing = Noisy;
drop(thing);
println!("1");
let thing = Noisy;
println!("2");
0
Dropped
1
2
Dropped

但是,作为oli_obk - ker points out,变量占用的堆栈内存在函数退出之前不会被释放,只有变量占用的资源才会释放。

所有关于drop的讨论都需要显示其(非常复杂的)实现:

fn drop<T>(_: T) {}
  

如果我在其他函数之外的全局范围内声明变量怎么办?

如果您甚至可以创建全局变量,那么它们永远不会被释放。

答案 1 :(得分:7)

在删除订单时, shadowing 覆盖变量之间存在差异。

所有局部变量通常在超出范围时被删除,与声明的顺序相反(参见 Rust编程语言&#39; s chapter on Drop)。这包括阴影变量。很容易通过将值包装在一个简单的包装器结构中来检查这一点,该结构在它(包装器)被删除时打印一些东西(只是之前删除值本身):

use std::fmt::Debug;

struct NoisyDrop<T: Debug>(T);

impl<T: Debug> Drop for NoisyDrop<T> {
    fn drop(&mut self) {
        println!("dropping {:?}", self.0);
    }
}

fn main() {
    let hello = NoisyDrop("Hello");
    let hello = NoisyDrop("Goodbye");

    println!("My variable hello contains: {}", hello.0);
}

打印以下内容(playground):

My variable hello contains: Goodbye
dropping "Goodbye"
dropping "Hello"

那是因为范围中新的let绑定不会覆盖以前的绑定,所以它就像你写的那样

    let hello1 = NoisyDrop("Hello");
    let hello2 = NoisyDrop("Goodbye");

    println!("My variable hello contains: {}", hello2.0);

请注意,此行为与以下表面上非常相似的代码(playground)不同:

fn main() {
    let mut hello = NoisyDrop("Hello");
    hello = NoisyDrop("Goodbye");

    println!("My variable hello contains: {}", hello.0);
}

不仅会以相反的顺序丢弃它们,而是在打印消息之前删除第一个值!这是因为当您分配给变量(而不是使用新变量进行遮蔽)时,在移入新值之前,原始值将首先首先删除

我首先说局部变量是&#34;通常&#34;当他们超出范围时掉线。因为您可以将值移入和移出变量,所以在运行时有时无法进行分析以确定何时需要被丢弃。在这种情况下,编译器实际上是inserts code to track "liveness" and drop those values when necessary,因此您不能通过覆盖值而意外地导致泄漏。 (但是,通过调用mem::forget或通过创建具有内部可变性的Rc循环,仍然可以安全地泄漏内存。)