前言

最近在学习 RustRust 中也有引用,这让我有点好奇 Rust 的引用是不是与 C++ 一样,于是便有了此文。

变量的引用

C++

先来看看 C++ 中变量的引用

1
2
3
4
int a = 1;
int& ref = a;
std::cout << "a的地址: " << &a << std::endl;
std::cout << "引用ref的地址: " << &ref << std::endl;

结果如下:

a的地址: 0x7ff7b28663e8
引用ref的地址: 0x7ff7b28663e8

可以看到引用 ref 的地址和 a 的地址一模一样,所以在 C++引用只是个别名 和原来没什么区别。

Rust

1
2
3
4
let a: i32 = 1;
let refa: &i32 = &a;
println!("a的地址: {:p}", &a);
println!("引用refa的地址 :{:p}", &refa);

结果如下:

1
2
a的地址: 0x7ff7ba577df4
引用refa的地址 :0x7ff7ba577df8

可以看到引用 refa 的地址与 a 地址不同。也就是 refa 有它自己的地址,所以 Rust 的引用更像是指针,指针有它自己的地址,但是它们指向的内存是同一块。传递引用就是赋值,不可变引用是 Copy 语义。也就是通常说的值拷贝。

我们尝试把引用的内容打出来看下,猜测应该就是 a 地址

1
2
3
4
5
let a: i32 = 1;
let refa: &i32 = &a;
println!("a的地址: {:p}", &a);
println!("引用refa的地址: {:p}", &refa);
println!("引用refa指向值: {:p}", refa);

结果如下:

1
2
3
a的地址: 0x7ff7b677e1fc
引用refa的地址: 0x7ff7b677e1a8
引用refa指向值: 0x7ff7b677e1fc

从上面的结果中发现引用 refa 的内容(指向的地址)和 a 的地址一样,也就是它们指向同一块内存。而引用 refa 本身有它自己的地址。

引用作为函数参数

C++

依照惯例依旧先看下 C++ 中函数中使用引用作为参数的情况

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void test(int& a) {
    std::cout << "引用a的地址: " << &a << std::endl;
}

int main(int argc, char *argv[])
{
    int a = 1;
    std::cout << "a的地址: " << &a << std::endl;
    test(a);
    return 0;
}

结果如下:

a的地址: 0x7ff7b7cad3dc
引用a的地址: 0x7ff7b7cad3dc

函数调用的情况下 C++ 中引用的地址始终如一。没有发生变量的拷贝。

Rust

接下来看 Rust 中函数使用引用作为参数的情况

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fn test(a: &i32) {
    println!("参数a的地址: {:p}", &a);
    println!("参数a的指向的地址: {:p}", a);
}

fn main() {
    let a: i32 = 1;
    let refa: &i32 = &a;
    println!("a的地址: {:p}", &a);
    println!("refa的地址: {:p}", &refa);
    test(&a);
}

结果如下:

1
2
3
4
a的地址: 0x7ff7bb8bb1ac
refa的地址: 0x7ff7bb8bb1a0
参数a的地址: 0x7ff7bb8bb198
参数a的指向的地址: 0x7ff7bb8bb1ac

Rust 的函数中引用的地址与函数外面的引用 refa 都不一样,所以 Rust 中的引用更像是值传递而不是引用传递(相对C++而言)。

严格的说 Rust 的引用并不是真的引用,而是值拷贝。

总结

  • Rust 中的不可变引用实现的是 Copy 语义,在发生变量赋值、参数传递的情况下会发生拷贝。
  • Rust 可变引用没有实现 Copy 语义,发生赋值会转移所有权。
  • Rust 的引用不像 C++ 中引用,更像是指针。 Rust 的引用与 Golang 中引用更像。