stack & heap

在stack上的数据必须是 fixed size,在rust中,指的是实现了Sized这个trait的类型。如果类型的大小在编译时未知,或者大小会变,那这个变量就必须放在堆上

Ownership Rules

  • 所有的数据都有一个变量是它的owner
  • 同一时间只有一个owner
  • 当owner出了作用域,值就会被回收

String type

  1. let a = "hello";

这个代码片段中, a 是一个string 字面量,它并不是分配在堆上,也不在栈上,而是在data section, 这种类型是不可变的,所以rust中,还有一种类型叫做 String, 他是分配在堆上的

  1. let mut s = String::from("hello");
  2. s.push_str(", world")
  3. println!("{}", s);

对于字面量来说,我们在编译时就知道他的长度,所以字面量存放在最终二进制的数据区,而对于String来说,分配在堆上就意味着,我们需要在运行时请求资源,并且在我们用完这个变量后释放内存。

rust没有gc,也没有手动free (非unsafe代码), 所以,内存在他的owner出了作用域后,就会自动回收

move

  1. let s1 = String::from("hello");
  2. let s2 = s1;

image.png

但这样带来一个问题,当s1, s2,被释放的时候就出现了double free,这个是c语言中镜像会碰到的情况,rust是如何处理这种情况的?在rust中,同一时间只有一个owner,这个时候我们说 s1 move into s2, 所以当 let s2 = s1; 后,s1就无效了

  1. let s1 = String::from("hello");
  2. let s2 = s1;
  3. println!("{} world", s1);

以上代码片段无法编译通过

  1. fn main() {
  2. let s = String::from("hello");
  3. take_ownership(s);
  4. println!("{}", s); // s has been moved into fn `take_ownership`
  5. }
  6. fn take_ownership(s: String) { // s comes into scope
  7. println!("{}", s);
  8. } // s goes out of scope and `drop` is called. backing memory is freed.

以上代码也无法编译通过,那如果我需要在take_ownership后再使用 s 变量该怎么办?
一种方法是通过返回值

  1. fn main() {
  2. let s = String::from("hello");
  3. let s2 = take_ownership(s);
  4. println!("{}", s2); // s has been moved into fn `take_ownership`
  5. }
  6. fn take_ownership(s: String) -> String { // s comes into scope
  7. println!("{}", s);
  8. s
  9. } // s goes out of scope and `drop` is called. backing memory is freed.

return value

  1. fn gives_ownership() -> String {
  2. let s = String::from("hello");
  3. s
  4. }
  5. fn main() {
  6. let s1 = gives_ownership(); // gives_ownership moves its return value into s1
  7. }

reference & borrowing

我们也可以通过 reference来获取变量

  1. fn main() {
  2. let mut s = String::from("hello");
  3. let len = calculate_len(&s);
  4. println!("the length of {} is {}", s, len);
  5. }
  6. fn calculate_len(s: &String) -> usize { // s is a reference to a String
  7. s.len()
  8. } // here, s goes out of scope, but it does not have ownership, nothing happens

这里我们看到在获取len的时候,我们并不需要对s进行 deref操作,那是由于rust 隐式调用了 dereference operator,rust中很多操作符都可以重新实现