Rust中的引用类型
本节简单介绍Rust中的引用,混个脸熟,后面会专门详细介绍引用以及引用更细节更底层的内容。
Rust中,使用&T表示类型T的引用类型(reference type)。
例如,&String表示String的引用类型,&i32表示i32的引用类型,&&i32表示i32引用的引用类型。
引用类型是一种数据类型,它表示其所保存的值是一个引用。
值的引用写法和引用类型的写法类似。例如&33表示的是33这个值的引用。
引用,通常来说是指向其他数据的一个指针或一个胖指针(有额外元数据的指针)。例如&33表示的是一个指向数据值33的一个指针。
因此,引用类型保存值的引用。
例如:
#![allow(unused)]
fn main() {
let n: &i32 = &33_i32;
}
这里变量n的类型是引用类型&i32,它所保存的值必须是i32类型数据的引用,例如上面的&33_i32就是33_i32的引用。
可以将保存了引用的变量赋值给其他变量,这样就有多个变量拥有同一份数据的引用。
fn main(){
let n = 33;
let n_ref1 = &n; // n_ref1指向33
let n_ref2 = n_ref1; // n_ref2也指向33
}
可以使用std::ptr::eq()来判断两个引用是否指向同一个地址,即判断所指向的数据是否是同一份数据。
fn main(){
let n = 33;
let n_ref1 = &n;
let n_ref2 = n_ref1;
println!("{}", std::ptr::eq(n_ref1, n_ref2)); // true
}
可变引用
直接使用&创建出来的引用是只读的,这意味着可以通过该引用去读取其指向的数据,但是不能通过引用去修改指向的数据。
如果想要通过引用去修改源数据,需要使用&mut v来创建可修改源数据v的可变引用。
注意,想要通过&mut引用去修改源数据,要求原变量是可变的。这很容易理解,&mut是一个对源数据的引用,如果源数据本身就不允许修改,当然也无法通过&mut去修改这份数据。
因此,使用&mut的步骤大致如下:
#![allow(unused)]
fn main() {
let mut x = xxxx;
let x_ref = &mut x;
}
例如,下面声明的变量n是不可变的,即使创建&mut n,也无法修改原始数据。实际上,这会导致编译错误。
fn main(){
let n = 33;
let n_ref = &mut n; // 编译错误
}
因此,改为如下代码可编译通过:
fn main(){
let mut n = 33;
let n_ref = &mut n;
}
解引用
解引用表示解除引用,即通过引用获取到该引用所指向的原始值。
解引用使用*T表示,其中T是一个引用(如&i32)。
例如:
fn main(){
let s = String::from("junma");
let s_ref = &s; // s_ref是指向"junma"的一个引用
// *s_ref表示通过引用s_ref获取其指向的"junma"
// 因此s和*s_ref都指向同一个"junma",它们是同一个东西
assert_eq!(s, *s_ref); // true
}
再例如:
fn main(){
let mut n = 33;
let n_ref = &mut n;
n = *n_ref + 1;
println!("{}", n);
}
Rust绝大多数时候不会自动地解除引用。但在某些环境下,Rust会自动进行解引用。
自动解引用的情况有(结论先总结在此,混脸熟,以后涉及到时再来):
- (1).使用
.操作符时(包括取属性值和方法调用),会隐式地尽可能解除或创建多层引用 - (2).使用比较操作符时,若比较的两边是相同类型的引用,则会自动解除引用到它们的值然后比较
对于(1),Rust会自动分析func()的参数,并在需要的时候自动创建或自动解除引用。例如以abc.func()有可能会自动转换为&abc.func(),反之,&abc.func()也有可能会自动转换为abc.func()。
对于(2),例如有引用类型的变量n,那么n > &30和*n > 30的效果是一样的。