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