我在Rust的书中看到,你可以定义两个同名的变量:
let hello = "Hello";
let hello = "Goodbye";
println!("My variable hello contains: {}", hello);
字符串
这将输出:
My variable hello contains: Goodbye
型
第一个hello会发生什么?它会被释放吗?我怎么才能访问它?
我知道给两个变量命名相同是不好的,但是如果因为我在100行以下声明它而意外发生这种情况,那将是一个真实的痛苦。
4条答案
按热度按时间44u64gxh1#
Rust没有垃圾收集器。
Rust是否会释放被覆盖变量的内存?
是的,否则就是内存泄漏,这将是一个非常糟糕的设计决策。当变量被重新赋值时,内存被释放:
个字符
第一个hello会发生什么
它是有阴影的。
变量引用的数据不会发生任何“特殊”的情况,只是您无法再访问它。当变量超出范围时,它仍然会被删除:
的字符串
另请参阅:
我知道如果两个变量的名字相同
这不是“坏”,这是一个设计决策。我想说,像这样使用阴影是一个坏主意:
型
对我来说,这样使用阴影是合理的:
型
另请参阅:
但如果这是偶然发生的,因为我声明它低于100行,这可能是一个真实的的痛苦。
不要有太大的函数,以至于你“不小心”做了一些事情。这适用于任何编程语言。
是否有手动清除内存的方法?
您可以调用
drop
:但是,正如oli_obk - ker所指出的,在函数退出之前,变量占用的堆栈内存不会被释放,只有变量占用的资源才会被释放。
所有关于
drop
的讨论都需要展示它的(非常复杂的)实现:型
如果我在其他函数之外的全局作用域中声明变量呢?
全局变量永远不会被释放,即使你可以创建它们。
vc6uscn92#
当涉及到删除顺序时,shadowing 和 reassigning(重分配)变量之间有区别。
所有局部变量在超出作用域时通常会被删除,按照声明的相反顺序(参见 The Rust Programming Language 的chapter on
Drop
)。这包括隐藏变量。通过将值 Package 在一个简单的 Package 器结构中,当它( Package 器)被删除时(就在 * 值本身被删除之前)打印一些东西,可以很容易地检查这一点:字符串
打印以下内容(playground):
型
这是因为作用域中的新
let
绑定不会覆盖以前的绑定,所以就像您编写了型
请注意,此行为与下面的代码(playground)不同,表面上非常相似:
型
这不仅会以相反的顺序删除它们,而且会在打印消息之前删除第一个值!这是因为当你给一个变量赋值时(而不是用一个新的变量来隐藏它),原始值会在新值被移入之前被 * 首先 * 删除。
我开始说局部变量在超出作用域时“通常”会被删除。因为你可以在变量中移入或移出值,所以有时候直到运行时才能分析出什么时候需要删除变量。在这种情况下,编译器实际上是inserts code to track "liveness" and drop those values when necessary,所以你不会意外地因为变量的值而导致泄漏。(但是,仍然可以通过调用
mem::forget
或创建具有内部可变性的Rc
-cycle来安全地泄漏内存。参见
0pizxfdo3#
这里有几点需要注意:
在你给出的程序中,当编译它时,“Hello”字符串没有出现在二进制文件中。这可能是编译器优化,因为第一个值没有使用。
字符串
然后测试:
型
请注意,如果打印第一个值,字符串会出现在二进制文件中,因此这证明这是编译器优化,不存储未使用的值。
另一件需要考虑的事情是,分配给
hello
的两个值(即“Hello”和“Goodbye”)都有一个&str
类型。这是一个指向编译后静态存储在二进制文件中的字符串的指针。动态生成字符串的一个例子是当您从某些数据生成哈希值时,如MD5或SHA算法(结果字符串不静态存在于二进制文件中)。型
这意味着变量只是指向静态内存中的一个位置。在运行时不会分配内存,也不会释放内存。即使不存在上面提到的优化(即省略未使用的字符串),只有
hello
指向的内存地址位置会改变,但内存仍然被静态字符串使用。对于
String
类型,情况会有所不同,请参考其他答案。kh212irz4#
我相信这个问题已经得到了彻底的回答,但我想给予更多的了解,因为我有一个similar question:
1.问题中的示例代码只包含
&str
到程序二进制中的字符串常量,如这里和这里所解释的。没有什么需要清理的。1.这些值被隐藏而不是被替换,但是...
1.如果考虑
String::from(...)
s并重新分配相同的变量,this Rustonomicon article解释了何时删除值。