所有权机制简介
为什么引入所有权
在C语言中,可以使用malloc分配一定大小的内存,再使用free释放分配的内存。这样做存在一个问题,就是当我们把指针传递给一个函数,在函数返回值之后,无法确定指针指向的内存是否已经被释放。函数中有可能已经释放过内存,如果再释放一遍就会出现问题。Rust引入了所有权机制,可以规避这个问题。
什么是所有权
简单来说,所有的变量都有一个所有者(Owner),一般是指所在的函数。当一个函数结束时,除了返回值之外的其他变量都会drop掉,也就是被释放掉。另外,当我们把变量作为参数传递给另一个函数时,这个变量的所有权会发生改变,传递给被调用的函数,在原函数中不能再使用该变量。
代码
#[derive(Copy, Clone)]
struct Foo {
x: i32,
y: usize,
z: Option<f64>,
}
fn main() {
let x = 42;
let y = x;
println!("{x}");
let x = Box::new(42);
let y = x.clone();
println!("{x}");
let foo1 = Foo { x: 42, y: 123, z: None };
let foo2 = foo1;
println!("{}", foo1.x);
let ptr = give_me_int();
print_int(ptr.clone());
println!("{ptr}");
}
fn print_int(ptr: Box<i32>) {
println!("{ptr}");
// drop(ptr);
}
fn give_me_int() -> Box<i32> {
Box::new(42)
}
其中,Box起到分配内存的作用,drop相当于C语言中的free函数。
在give_me_int()函数中,一个值为42的i32类型变量被创建,第一个所有者是give_me_int()。
之后返回给main,所有者变为main,变量名是ptr。
再之后传递给print_int()函数。如果这里直接传递ptr,那么所有者会转变为print_int()。而print_int()不返回任何变量,会drop掉传入的ptr,因此ptr就此消失,在原先的main中将无法执行println!("ptr")。
因此使用了ptr.clone()方法,简单理解就是复制了一份传过去,这样跟原先的ptr就没有关系了。
值得注意的是,println!()不改变参数所有者。
这段代码还有一部分是关于Foo结构体,可以看到main函数中创建了foo1,又赋值给foo2,再之后又使用的foo1.x。可以这么做,是因为在定义Foo结构体时,用到#[derive(Copy, Clone)],允许Copy和Clone,因此foo1赋值给foo2时会有复制的过程,foo1仍旧拥有资源。如果不允许复制,那么foo1赋值给foo2后,foo1不再被允许访问资源x。
Match 关键字
match相当于C语言中的switch语句,写法略有不同。使用match时应当注意default情况,使用的是下划线 _ 作为default情况,类似python中 _ 的用法。
let a = 1;
match a {
1 => { println!("1"); }
2 => { println!("2"); }
_ => { println!("3"); }
}
match还可以用于赋值等。
添加库
在Rust中库被称为crate。打开Cargo.toml文件,可以看到[dependencies],这部分就是已经添加的库的名称,可以使用如下命令添加。
cargo add yourcratename
标准输入输出
std::io::stdin()和std::io::stdout()可以创建两个对象,使用对象中的方法可以进行标准输入输出。可以参考stdin in std::io - Rust和stdout in std::io - Rust
在开始执行程序时也可以输入参数,可以使用std::env::args()创建的对象获取。参考args in std::env - Rust