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
的效果是一样的。