All programs have to manage the way they use a computer’s memory while running. Some languages have garbage collection that constantly looks for no longer used memory as the program runs; in other languages, the programmer must explicitly allocate and free the memory. Rust uses a third approach: memory is managed through a system of ownership with a set of rules that the compiler checks at compile time. None of the ownership features slow down your program while it’s running.
关于stack和heap
All data stored on the stack must have a known, fixed size. Data with an unknown size at compile time or a size that might change must be stored on the heap instead.
关于Rust的GC
{
let s = String::from("hello"); // s is valid from this point forward
// do stuff with s
} // this scope is now over, and s is no
// longer valid
Rust takes a different path: the memory is automatically returned once the variable that owns it goes out of scope.
There is a natural point at which we can return the memory our String needs to the allocator: when s goes out of scope.
Move
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1); // error
We call having references as function parameters borrowing
在传入reference的时候,如果想要修改对应reference的值,可以添加mut
fn main() {
let mut s = String::from("hello");
println!("original string: {}", s);
print_string(&mut s);
println!("after change in print string: {}", s); // fine
let x = 5;
makes_copy(x);
println!("{}", x); // fine
}
fn print_string(some_string: &mut String) { // 接受一个&mut String类型的string
some_string.push_str(", world");
println!("{}", some_string);
}
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
}
But mutable references have one big restriction: you can have only one mutable reference to a particular piece of data in a particular scope.
fn main () {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // error: cannot borrow `s` as mutable more than once at a time
println!("{}, {}", r1, r2);
}
The benefit of having this restriction is that Rust can prevent data races at compile time.
fn main () {
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{} and {}", r1, r2);
// r1 and r2 are no longer used after this point
let r3 = &mut s; // no problem
println!("{}", r3);
}
Dangling Pointer
fn dangle() -> &String { // dangle returns a reference to a String
let s = String::from("hello"); // s is a new String
&s // we return a reference to the String, s
} // Here, s goes out of scope, and is dropped. Its memory goes away.
// Danger!
Ownership
关于内存和GC的说明
关于stack和heap
关于Rust的GC
Move
当S1定义后,S2跟S1指向同一个heap variable。
正常情况,gc时,会去gc s1,然后gc s1对应的heap variable。再去gc s2的时候,s2指向的heap variable已经被gc了。就会导致memory corruption。
为了解决这个问题,Rust采用一种叫做move的策略。当s1定义完,s2指向s1的时候,s1自动变成invalid,即不再有效。
针对stack-only data type,这些type会有一个特殊的anotation叫做Copy的Trait,具有copy trait的类型,可以在move之后继续使用。
Drop
当一个变量out of scope,Rust会自动call一个叫drop的function来帮助gc。
第二行定义了s,调用
takes_ownership
,在第10行打印完后,some_string会out of scope, 因此被drop,第四行定义了x,integer是拥有Copy Trait Anotation的类型,所以可以继续使用
想要解决第四行这个问题,可以让takes_ownership函数返回一个string,从而达到Transfer Ownership的意义。
Reference and Borrowing
同时上述问题可以使用reference来解决。
reference不会被不拥有ownership的scope drop掉,例如print_string不拥有some_string的ownership,那他就不能drop,只有在main函数执行完才会被drop
在传入reference的时候,如果想要修改对应reference的值,可以添加mut
Dangling Pointer