在Rust中,我们可以隐藏一个变量,创建一个具有相同名称的新变量。我们看下面这个例子:
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x in the inner scope is: {x}"); // 12
}
println!("The value of x is: {x}"); // 6
}
我认为原来的变量x被删除了,它的内存被释放了。但事实并非如此;该变量继续存在,并且它的内存不会被释放,即使不能通过其名称访问该值。
当使用Rc指针时,新的疑问出现了,注意到Rc指针的clone会增加ref计数器。看看这个例子:
use std::rc::Rc;
fn main() {
let a = Rc::new(5);
let ptr = Rc::as_ptr(&a);
let a = a.clone(); // 显示一个递增计数器
assert_eq!(Rc::strong_count(&a), 2);
let b = a.clone();
assert_eq!(Rc::strong_count(&a), 3);
std::mem::drop(a); // 递减计数器
assert_eq!(Rc::strong_count(&b), 2);
std::mem::drop(b);
unsafe { assert_eq!(*ptr, 5) }; // 它在堆里存活吗?
}
我们看看下面这个例子,shadowing并不等同于dropping:
struct LoudDrop(&'static str);
impl Drop for LoudDrop {
fn drop(&mut self) {
println!("{} dropped", self.0);
}
}
fn main() {
let a = LoudDrop("a1");
let a = LoudDrop("a2");
drop(a);
println!("End of main");
}
这段代码的输出结果如下:
a2 dropped
End of main
a1 dropped
我们可以看到,原始的a被遮蔽了,但它在main()函数结束之前是存活的。
我们再看下面这个例子:
use std::rc::Rc;
fn main() {
let ptr;
{
let a = Rc::new(5);
ptr = Rc::as_ptr(&a);
} //a 在这里被释放
unsafe { assert_eq!(*ptr, 5) }; // 内存中的值仍然是5
}
当执行到代码unsafe{assert_eq!(*ptr, 5)}时,它不会检查值是否为“alive”。它检查内存中指针处的值,但不知道那个地址处的内存是否被分配了。释放内存并不会清理内存。清理内存的操作是昂贵的,所以编译器通常会避免它并留下垃圾值。”
我们再看另一个例子:
fn main() {
let i32_ptr: *const i32;
let f64_ptr: *const f64;
let char_ptr: *const char;
{
let x: i32 = 1;
i32_ptr = &x;
let x = 3.14;
f64_ptr = &x;
let x = 'a';
char_ptr = &x;
}
unsafe {
assert_eq!(*i32_ptr, 1);
assert_eq!(*f64_ptr, 3.14);
assert_eq!(*char_ptr, 'a');
}
}
在上面的例子中,所有变量都不是存活的,但是assert_eq!仍然可以工作。