移动(move): 多个变量可以在 Rust 中以不同的方式与相同的数据交互
let x=3; let y=x;
这里的数据是基本数据类型,所以不需要存到堆中,只在栈中复制和移动。
基本数据类型:
1.所有整数类型,例如 i32 、 u32 、 i64 等。
2.布尔类型 bool,值为 true 或 false 。
3.所有浮点类型,f32 和 f64。
4.字符类型 char。
5.仅包含以上类型数据的元组(Tuples)。
如果发生的情况在堆里面,就会不一样:
let s1=String::from("hello");
let s2=s1;
此处hello需要存在堆里面,后面释放的时候,并不会释放两次,因为s2=s1时,s1已经无效了,后面无法使用。
克隆(clone): 但如果需要将数据单纯的复制一份以供他用,可以使用数据的第二种交互方式。
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
}
这里释放资源的时候,会被当成两个资源释放。
fn main() {
let s = String::from("hello");
// s 被声明有效
takes_ownership(s);
// s 的值被当作参数传入函数
// 所以可以当作 s 已经被移动,从这里开始已经无效
let x = 5;
// x 被声明有效
makes_copy(x);
// x 的值被当作参数传入函数
// 但 x 是基本类型,依然有效
// 在这里依然可以使用 x 却不能使用 s
} // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放
fn takes_ownership(some_string: String) {
// 一个 String 参数 some_string 传入,有效
println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放
fn makes_copy(some_integer: i32) {
// 一个 i32 参数 some_integer 传入,有效
println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放
也就是说,如果函数变量当作参数传入函数,他和移动的效果是一样的。
fn main() {
let s1 = gives_ownership();
// gives_ownership 移动它的返回值到 s1
let s2 = String::from("hello");
// s2 被声明有效
let s3 = takes_and_gives_back(s2);
// s2 被当作参数移动, s3 获得返回值所有权
} // s3 无效被释放, s2 被移动, s1 无效被释放.
fn gives_ownership() -> String {
let some_string = String::from("hello");
// some_string 被声明有效
return some_string;
// some_string 被当作返回值移动出函数
}
fn takes_and_gives_back(a_string: String) -> String {
// a_string 被声明有效
a_string // a_string 被当作返回值移出函数
}
被当作函数返回值的变量所有权将会被移动出函数,并返回到调用函数的地方,而不会被无效释放。
引用其实可以当成是指针,是变量的间接访问方式
fn main(){
let s1=String::from("hello");
let s2=&s1;
println!("s1 is {},s2 is {}",s1,s2);
}
这里的s1并不会被无效,s1个地址,它指向堆中的量,s1把地址给s2,s2指向s1。
记住这张图,s2=&s1。。当然在函数中传递引用参数,也是和这里一样的效果。
引用不会获得值的所有权,引用只是租借值的所有权。在引用之后s2=&s1,如果s3=s1,也就是将s1移动到s3,这时s2就无法租借s1而失效,需要重新租借s3。
同时,也不可以租借之后对值进行修改,这个和租房子一个道理,未经房东允许,不可以改变房子。
但是,也可以房东允许你装修:
fn main(){
let mut s1=String::from("run");
let s2=&mut s1;
s2.push_str("oob");
println!("{}",s2);
}
这里的&mut修饰的是可变的引用类型。
提出一个很重要的概念:同时写内存造成数据不一致的问题。
可变引用不允许多重引用,但是不可变引用可以。
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
这段代码有问题,因为多重可变引用了s。而rust对于这种设计就是出于对并发状态下发生数据访问碰撞的考虑,防止同时写进行修改数据造成数据的不一致性。
定义:指的是那种没有实际指向一个真正能访问的数据的指针(注意,不一定是空指针,还有可能是已经释放的资源)。这个就是C语言里面说的“野指针”,任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
在Rust语言里面不允许出现,如果有,编译器会发现它。