likes
comments
collection
share

【Rust 中级教程】 14 引用与借用(2)本篇文章将继续介绍 Rust 的引用,主要介绍一些引用的基本特征。本篇文章

作者站长头像
站长
· 阅读数 43

0x00 开篇

本篇文章将继续介绍 Rust 的引用,主要介绍一些引用的基本特征。本篇文章的阅读时间大约 5 分钟

0x01 println! 所有权?

本篇文章先来讨论一个问题,其实这也是大家比较关注的。

Q:println! 执行后变量为什么没有失去所有权?

要解开上面两个问题,我们搞清楚 println! 内部是如何执行的。println! 其实是一个宏,宏在编译时会扩展,被替换为其它 Rust  代码。(后续章节会详细介绍宏)想要看展开后的代码也很简单,只需要在源码根目录运行 cargo expand

先看这段代码:

fn main() {
    let mut m = String::from("rust");
    // 打印 m 和 n(没有解引用)
    println!("m = {}", m);
    // m 追加字符串(没有失去所有权)
    m.push_str(" is easy.");
    // 打印 m
    println!("{}", m);
}


// 运行结果
// m = rust
// rust is easy.

我们在使用 println! 打印 m 时,m 并没有失去所有权, 执行下面的命令:

cargo expand

展开后的代码如下:

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
    let mut m = String::from("rust");
    {
        ::std::io::_print(
            ::core::fmt::Arguments::new_v1(
                &["m = ", "\n"],
                // 传递了 &m
                &[::core::fmt::ArgumentV1::new_display(&m)],
            ),
        );
    };
    m.push_str(" is easy.");
    {
        ::std::io::_print(
            ::core::fmt::Arguments::new_v1(
                &["", "\n"],
                // 传递了 &m
                &[::core::fmt::ArgumentV1::new_display(&m)],
            ),
        );
    };

通过展开后的代码,可以看到 println!() 传递的是引用的值,所以它不会失去所有权了。但是如果你使用 dbg! 来打印变量的时候将会失去所有权,因为 dbg! 并没有获取参数的引用值,大家可以自行测试。

0x02 引用的引用

在 Rust 中允许  ”引用的引用“  这种“套娃”存在。

fn main() {
    let a: i32 = 4;
    let b: &i32 = &a;
    let c: &&i32 = &b;
    let d: &&&i32 = &c;
    println!("a = {}, b = {}, c = {}, d = {}", a, b, c, d);


    struct Rectangle {
        w: u32,
        h: u32,
    }
    let x: Rectangle = Rectangle { w: 3, h: 4 };
    let y: &Rectangle = &x;
    let z: &&Rectangle = &y;


    println!("x: w = {}, h = {}", x.w, x.h);
    println!("y: w = {}, h = {}", y.w, y.h);
    println!("z: w = {}, h = {}", z.w, z.h);
}
// 运行结果
// a = 4, b = 4, c = 4, d = 4
// x: w = 3, h = 4
// y: w = 3, h = 4
// z: w = 3, h = 4

上面的代码,我标注了类型,其实 Rust 是可以推断的,可以省略。不管“套娃”多少次, . 操作符,Rust 都可以找到他所引用的原始值。

0x03 引用的比较

fn main() {
	let m = 1;
    let n = 2;
    let m1 = &m;
    let n1 = &n;


    // 比较
    if m1 < n1 {
        println!("m1 > n1");
    }
    
    // 下面的代码错误
    // if m1 < n { }
}
// 运行结果
// m1 < n1

当引用比较时,无论 “套娃” 多少次,同样也可以使用最终指向的值进行比较。但是比较时一定要注意类型必须相同,Rust 不同的类型默认是无法比较的。如:m1 < n 这种语法是错误的,因为 m1 是 &i32类型, n 是 i32 类型。

0x04 对表达式的引用

fn main() {
    // 普通表达式
	let x = &(8 + 78);
    println!("x = {}", x);


    // 闭包
    let f = |h| h + 1;
    let y = &f(1);
    println!("y = {}", y);


    // 函数
    fn func_test(a: i32) -> i32 {
        return a + 2;
    }
    let z = &func_test(3);
    println!("y = {}", z);
}
// 运行结果
// x = 86
// y = 2
// y = 5

Rust 中允许对表达式进行引用。Rust 会创建一个匿名的变量来保存表达式的值,然后生成一个指向该值的引用

0x05 小结

本篇文章主要介绍了 Rust 引用的一些特征属性,但是要注意的是,Rust 中引用永远不会为空。所以并不会存在类似其他语言的空指针的类型。本文介绍的内容相对简单,另外 println! 和 dbg! 对传参处理方法的不同也需要额外注意。

转载自:https://juejin.cn/post/7223043596621201464
评论
请登录