【Rust 中级教程】 14 引用与借用(2)

简介: 【Rust 中级教程】 14 引用与借用(2)

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类型, ni32 类型。


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! 对传参处理方法的不同也需要额外注意。

相关文章
|
2月前
|
Rust 安全 编译器
Rust中的生命周期与借用检查器:内存安全的守护神
本文深入探讨了Rust编程语言中生命周期与借用检查器的概念及其工作原理。Rust通过这些机制,在编译时确保了内存安全,避免了数据竞争和悬挂指针等常见问题。我们将详细解释生命周期如何管理数据的存活期,以及借用检查器如何确保数据的独占或共享访问,从而在不牺牲性能的前提下,为开发者提供了强大的内存安全保障。
|
3月前
|
设计模式 Rust JavaScript
【一起学Rust | 设计模式】习惯语法——使用借用类型作为参数、格式化拼接字符串、构造函数
【一起学Rust | 设计模式】习惯语法——使用借用类型作为参数、格式化拼接字符串、构造函数
37 0
|
2月前
|
Rust 安全 编译器
Rust中避免常见错误:悬挂引用与生命周期不匹配
本文深入探讨了Rust编程语言中常见的两个内存管理错误:悬挂引用和生命周期不匹配,并提供了避免这些错误的实用方法。我们将详细解释这两种错误的来源,并通过示例展示如何在Rust中通过正确的生命周期标注和借用规则来避免它们,从而确保代码的内存安全性。
|
4月前
|
存储 Rust 安全
Rust 中的引用与借用
Rust 中的引用与借用
|
5月前
|
Rust 安全 编译器
Rust 基础入门 —— 2.3.所有权和借用 (二)
同一时刻,你只能拥有要么一个可变引用, 要么任意多个不可变引用 引用必须总是有效的 贴一个体验不错的学习链接恰饭:学习链接
43 0
|
5月前
|
存储 Rust 安全
Rust 基础入门 —— 2.3.所有权和借用
写在前面的序言 因为我们这里实际讲述的内容是关于 内存安全的,所以我们最好先复习一下内存的知识。
25 0
|
5月前
|
Rust 安全 JavaScript
Rust教程初识
当然,它们都有编译然后执行可执行程序的用法。应当有编译后改名的方式,不然那也太不coooooooooooool了。
43 0
|
9月前
|
存储 消息中间件 Rust
Rust极简教程
Rust是一门赋予每个人构建可靠且高效软件能力的编程语言。可靠主要体现在安全性上。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。Rust 语言由 Mozilla 开发,最早发布于 2014 年 9 月。Rust 的编译器是在 MIT License 和 Apache License 2.0 双重协议声明下的免费开源软件。
243 0
Rust极简教程
|
存储 Rust JavaScript
Rust:为什么不能在同一个结构体中存储一个值和对该值的引用?(修改版)
基本把下面问题这个搞明白,就能彻底明白 Rust 语言的生命周期是怎么回事了。简而言之,生命周期不会改变你的代码,是你的生命控制生命周期,而不是生命周期在控制你的代码。换言之,生命周期是描述性的,而不是规定性的。
137 0
|
Rust 编译器
Rust 中级教程 第17课——引用的 lifetime(2)
Rust 中级教程 第17课——引用的 lifetime(2)
Rust 中级教程 第17课——引用的 lifetime(2)