引用Reference与指针Pointer基本概念

学习内容

  • 了解和学习引用Reference与指针Pointer概念

篇目

什么是指针Pointer

  Rust语言技术术语,既有其本身专门的技术术语,如软件篋crate、借用Borrowing和生命周期Lifttime等,也有一般计算机科学技术术语,如变量、类型和指针Pointer等。但是,对于一般技术术语,在Rust语言里也是有不同的内涵意义和实现形式。

  引用英文

In computer science, a pointer is a programming language object that stores the memory address of another value located in computer memory.

直接翻译:在计算机科学中,指针是编程语言的对象,用于存储位于计算机内存中的另一个值的内存地址。

解读:

  • 指针是一个对象object或者说实例instance,它是指针类型的对象;
  • 指针也是变量,与其它变量完全一样;
  • 指针变量也绑定一个值,与其它变量绑定不一样的值,它绑定一种特殊值,就是内存地址;
  • 在绑定的内存地址值下,也是一个变量;

什么是引用Reference

  引用德文

Eine Referenz ist ein Verweis auf ein Objekt. Eine Referenz ist damit ein Aliasname für ein bereits bestehendes Objekt.

直接翻译:引用是关于对象的参照物。因此引用是现有对象的别名。

解读:

  • 引用是总是与一个对象关联起来的;
  • 从本质上,引用就是相关联的对象,只是表现形式不同而已;

Rust语言的引用Reference与指针Pointer

  在Rust语言里,所有指针都是其自己的类型。且存在很多的指针pointer类型。比如,从Rust语言结构上分析,引用reference类型是一种指针类型,且最简单的指针类型,从内容上分析,每一个指针类型都包含引用的内存地址。引用reference类型的对象是最常见的。下面通过最简单的示意图了解什么是指针概念:

image

  从说明示意图结构上看到,引用reference类型的对象ref_u8与整数u8类型的对象instance是完全一样的,只是其值不同而已。

  但是Rust语言在处理其值的方法有所不同。这是因为,Rust通常专注于非指针对象的值或者指针对象的引用对象值(即内容中有趣的部分),而不是指针对象的标识(内存地址)。比如下面代码,使用宏println!简单格式打印它们的值都是一样的,只有使用特殊格式,才能打印出引用对象的值,即内存地址。


# #![allow(unused_variables)]
#fn main() {
let instance = 42_u8;
let ref_u8 = &instance;
println!("{}", instance);
println!("{}", ref_u8);
println!("{:p}", ref_u8);
#}

  Rust语言指针类型可以分为三大类:引用(包括共享引用和可变引用)、原始引用和智能引用smart pointer。其中前面两类类是Rust语言本身的,而第三类是属于标准库的。这里重点说明共享引用和原始引用。

  Ⓘ 共享引用指向其他值所拥有的内存。当创建共享引用值时,引用将防止该值的直接改变。除了下面说明的原始指针之外,Rust语言其他指针都是安全的,并且都具有其生命周期。

  Ⓘ 原始指针是没有安全性保证的指针。

  在Rust代码中通常不提倡和不鼓励使用原始指针。Rust语言的原始指针与C语言的指针是等效的。原始指针可以为null,也可以指向垃圾,它们也没有生命周期。

  为了以后说明问题简单化,我们把上面示意图统一成如下形式:

image

引用实例解释

image

  借助于上面两个引用和一个原始引用的示意图,理解下面相关代码的意义。

  在函数main()里,一共有四段代码,前面两段代码形成了上面示意图内容,后面两段代码是获取它们的地址,以便说明问题。


# #![allow(unused_variables)]
#fn main() {
    // File: lib-hello/src/immut/type_ref/mod.rs
    // Function use_references_simple()

    let instance: &str = "Hello";
    let instance = "Hello";

    let copy_instance: &str = instance;
    let copy_instance = instance;

    println!("instance reference address = {:p}", instance);
    println!("copy_instance reference address = {:p}", copy_instance);

    println!("instance address = {:p}", &instance);
    println!("copy_instance address = {:p}", &copy_instance);

#}

  第一段的两行代码是等效的,实际只需要一行代码就可以了。这代码把字符串文字绑定了变量instance同时,也形成了原始指针,变量instance的地址值指向了原始指针的地址。

  第二段的两行代码也是等效的,实际只需要一行代码就可以了。这代码把变量instance绑定到新变量copy_instance,这种绑定方式Rust语言称之为复制(Copy),这里它不会产生新原始指针,而是指向与变量instance相同的原始指针地址。

  从下面程序输出结果,也可以得到验证上面的阐述。第一行和第二行的地址就是原始指针的内存地址,而第三行和第四行的地址是两个引用变量自身的内存地址,注意,它们不是变量的内容值(内存地址)。

───────┬──────────────────────────────────────────────────────────────────────────
       │ STDIN
───────┼──────────────────────────────────────────────────────────────────────────
   1   │ instance reference address = 0x101c39b20
   2   │ copy_instance reference address = 0x101c39b20
   3   │ instance address = 0x7fff5dfe81a0
   4   │ copy_instance address = 0x7fff5dfe81c0
───────┴──────────────────────────────────────────────────────────────────────────

题外话

介绍Rust的指针类型列表

种类类型名称说明
共享引用&T引用Reference允许一个或者多个引用来类型T
可变引用&mut T可变引用Mutable Reference仅允许单个引用来读和写类型T
智能引用BoxBox指针处于堆上类型T的指针类型,该类型只能有单个所有者,它可以读取和写入类型T。
智能引用Rc参考计数指针处于堆上类型T的指针类型,该类型可以有多个所有者,它们可以读取类型T。
智能引用Arc核参考计数指针与类型Rc一样,但适用于在线程之间安全共享
原始引用*const T原始指针Raw pointer不安全地读取访问类型T
原始引用*mut T可变原始指针Mutable raw pointer不安全地读取和写入访问类型T

image

开发工具:软件篋prettytable-rs

  使用工具软件篋prettytable-rs可以使得程序输出更加美观。

  为了使用该工具,需要将下面代码放入文件Cargo.toml的[dependencies]段里:

prettytable-rs = "0.8.0"

  具体使用实例代码如下:

    // File: lib-hello/src/other/crate_tools/mod.rs
    // Function use_prettytable()

    let instance = "Hello";

    let copy_instance = instance;

    let table = table!(
        ["Name", "Value", "Remark"],
        [
            "instance reference address",
            format!("{:p}", instance),
            "is equal to the following line"
        ],
        [
            "copy_instance reference address",
            format!("{:p}", copy_instance),
            ""
        ],
        [
            "instance address",
            format!("{:p}", &instance),
            "is not equal to the following line"
        ],
        ["copy_instance address", format!("{:p}", &copy_instance), ""]
    );

    table.printstd();

  程序输出结果:

───────┬─────────────────────────────────────────────────────────────────────────────────────────────────────
       │ STDIN
───────┼─────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ +---------------------------------+----------------+------------------------------------+
   2   │ | Name                            | Value          | Remark                             |
   3   │ +---------------------------------+----------------+------------------------------------+
   4   │ | instance reference address      | 0x104f58ba0    | is equal to the following line     |
   5   │ +---------------------------------+----------------+------------------------------------+
   6   │ | copy_instance reference address | 0x104f58ba0    |                                    |
   7   │ +---------------------------------+----------------+------------------------------------+
   8   │ | instance address                | 0x7fff5ad3fa50 | is not equal to the following line |
   9   │ +---------------------------------+----------------+------------------------------------+
  10   │ | copy_instance address           | 0x7fff5ad3fa60 |                                    |
  11   │ +---------------------------------+----------------+------------------------------------+
───────┴─────────────────────────────────────────────────────────────────────────────────────────────────────

参考资料