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

Ale*_*una 14 memory-management rust

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

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

println!("My variable hello contains: {}", hello);
Run Code Online (Sandbox Code Playgroud)

打印出:

My variable hello contains: Goodbye
Run Code Online (Sandbox Code Playgroud)

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

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

She*_*ter 13

Rust没有垃圾收集器.

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

是的,否则它将是一个内存泄漏,这将是一个非常可怕的设计决定.

第一个问候会发生什么

它被遮蔽了.

变量引用的数据没有任何"特殊"发生,除了您无法再访问它的事实.当变量超出范围时,它仍然被丢弃.

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

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");
}
Run Code Online (Sandbox Code Playgroud)
0
1
2
Dropped
Dropped
Run Code Online (Sandbox Code Playgroud)

我知道将两个变量命名为相同是不好的

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

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

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

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

也可以看看:

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

没有那么大的功能,你"意外"做某事.这适用于任何编程语言.

有没有办法手动清理内存?

你可以打电话drop:

println!("0");
let thing = Noisy;
drop(thing);
println!("1");
let thing = Noisy;
println!("2");
Run Code Online (Sandbox Code Playgroud)
0
Dropped
1
2
Dropped
Run Code Online (Sandbox Code Playgroud)

但是,正如oli_obk - ker指出的那样,变量占用的堆栈内存在函数退出之前不会释放,只有变量占用的资源才会释放.

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

fn drop<T>(_: T) {}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 释放内存存在一个根本问题:堆栈可以(根据定义)仅在顶部(或底部,根据您的视图)进行修改.所以你不能在中间"释放"记忆.如果按顺序有3个变量`a`,`b`和`c`,你不能"释放"`b`,你可以停止使用它或重用它的内存,但不能释放它. (7认同)

tre*_*tcl 7

降序时,阴影覆盖变量之间存在差异.

所有局部变量在超出范围时通常会以与声明相反的顺序被删除(请参阅Rust编程语言章节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);
}
Run Code Online (Sandbox Code Playgroud)

打印以下(游乐场):

My variable hello contains: Goodbye
dropping "Goodbye"
dropping "Hello"
Run Code Online (Sandbox Code Playgroud)

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

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

    println!("My variable hello contains: {}", hello2.0);
Run Code Online (Sandbox Code Playgroud)

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

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

    println!("My variable hello contains: {}", hello.0);
}
Run Code Online (Sandbox Code Playgroud)

这不仅会以相反的顺序丢弃它们,而是在打印消息之前删除第一个值!这是因为当你分配给一个变量(而不是用一个新的阴影吧),原来的值被丢弃第一,之前的新值中移动.

我首先说当局部变量超出范围时"正常"掉线.因为您可以将值移入和移出变量,所以有时候直到运行时才能分析确定何时需要删除变量.在这种情况下,编译器实际上会插入代码来跟踪"活跃度"并在必要时删除这些值,因此您不会通过覆盖值而意外地导致泄漏.(但是,仍然可以通过调用mem::forget或通过创建Rc具有内部可变性的循环来安全地泄漏内存.)