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