stack & heap
在stack上的数据必须是 fixed size,在rust中,指的是实现了Sized这个trait的类型。如果类型的大小在编译时未知,或者大小会变,那这个变量就必须放在堆上
Ownership Rules
- 所有的数据都有一个变量是它的owner
- 同一时间只有一个owner
- 当owner出了作用域,值就会被回收
String type
let a = "hello";
这个代码片段中, a
是一个string 字面量,它并不是分配在堆上,也不在栈上,而是在data section, 这种类型是不可变的,所以rust中,还有一种类型叫做 String, 他是分配在堆上的
let mut s = String::from("hello");
s.push_str(", world")
println!("{}", s);
对于字面量来说,我们在编译时就知道他的长度,所以字面量存放在最终二进制的数据区,而对于String来说,分配在堆上就意味着,我们需要在运行时请求资源,并且在我们用完这个变量后释放内存。
rust没有gc,也没有手动free (非unsafe代码), 所以,内存在他的owner出了作用域后,就会自动回收
move
let s1 = String::from("hello");
let s2 = s1;
但这样带来一个问题,当s1, s2,被释放的时候就出现了double free,这个是c语言中镜像会碰到的情况,rust是如何处理这种情况的?在rust中,同一时间只有一个owner,这个时候我们说 s1 move into s2, 所以当 let s2 = s1;
后,s1就无效了
let s1 = String::from("hello");
let s2 = s1;
println!("{} world", s1);
以上代码片段无法编译通过
fn main() {
let s = String::from("hello");
take_ownership(s);
println!("{}", s); // s has been moved into fn `take_ownership`
}
fn take_ownership(s: String) { // s comes into scope
println!("{}", s);
} // s goes out of scope and `drop` is called. backing memory is freed.
以上代码也无法编译通过,那如果我需要在take_ownership后再使用 s
变量该怎么办?
一种方法是通过返回值
fn main() {
let s = String::from("hello");
let s2 = take_ownership(s);
println!("{}", s2); // s has been moved into fn `take_ownership`
}
fn take_ownership(s: String) -> String { // s comes into scope
println!("{}", s);
s
} // s goes out of scope and `drop` is called. backing memory is freed.
return value
fn gives_ownership() -> String {
let s = String::from("hello");
s
}
fn main() {
let s1 = gives_ownership(); // gives_ownership moves its return value into s1
}
reference & borrowing
我们也可以通过 reference来获取变量
fn main() {
let mut s = String::from("hello");
let len = calculate_len(&s);
println!("the length of {} is {}", s, len);
}
fn calculate_len(s: &String) -> usize { // s is a reference to a String
s.len()
} // here, s goes out of scope, but it does not have ownership, nothing happens
这里我们看到在获取len的时候,我们并不需要对s进行 deref操作,那是由于rust 隐式调用了 dereference operator,rust中很多操作符都可以重新实现