0x00 开篇
本篇文章将继续介绍 Rust 的引用。本篇文章将介绍引用作为函数/方法参数和返回值的一些用法,以及注意事项。由于函数/方法用法相同,所以本文示例代码将以函数为主。本篇文章的阅读时间大约 5 分钟。
0x01 引用作为函数/方法参数
当我们在传递非 Copy 标记的类型的参数时,往往会失去所有权。如果我们在执行某个函数或者方法时,不想失去所有权,我们可以定义引用参数传给函数/方法。
fn main() {
let mut a = String::from("hello");
print_string(&a);
// 向 a 中追加字符串
a.push_str(" rust!");
print_string(&a);
println!("println! 打印--- {}", a);
}
// 打印一个字符串
fn print_string(s: &String) {
println!("print_string 打印--- {}", s);
}
我们创建了一个 print_string 函数,接收的参数是引用类型的。然后声明字符串变量 a, 将 a 的引用传入该函数。即使在后面再次使用 a ,它也不会失去所有权。当引用离开作用域时,将会归还所有权。
另外,值得注意的一点是 当进行解引用时,将会重新获得所有权。
下面的操作是错误的!!!!!!!!!!!!!
fn main() {
let aa = String::from("xxx");
operate_string(&aa);
}
fn operate_string(aa: &String) {
// 下面的代码是错误的
// let x = *aa;
}
上面的操作是错误的!!!!!!!!!!!!!
“当一个函数/方法接收引用时,我们在函数/方法内对参数解引用。” 假设这种做法是可行的,aa 的所有权被转移给 x, 那么函数/方法执行结束后,x销毁失去所有权,外部的 aa 将指向未知的位置,aa 将会变成野指针。所以此做法是被禁止的,编译将会发生错误。
0x02 引用作为函数/方法返回值
同样的,引用还可以作为函数/方法的返回值来使用。
fn main() {
let mut b = String::from("hello world");
let c = get_self(&mut b);
println!("c = {}", c);
}
fn get_self(s: &String) -> &String {
return s;
}
// 运行结果
// c = hello world
上面仅仅是一个很简单的例子,我们将在后面的 lifetime 章节中详细介绍。
0x03 胖指针 (Fat Pointer)
我们前面介绍的引用都是对一个值比较简单的引用,引用的仅仅是一个地址。胖指针也是一种引用,但是它包含两部分
- 某个值的地址
- 使用该值的相关必要信息
Rust 胖指针有两种,切片(slice)和特型对象(trait object) 。切片通常包含切片地址和长度信息,如最常见的 &str。对于特型对象,是对实现某种特型的一个值的引用。特型对象包含某个值的地址和一个指向与该值匹配的特型实现的指针。PS:我即将在后面章节中介绍。
普通引用和胖指针的区别
普通引用与胖指针唯一的区别就是**胖指针携带了额外的信息,占用两个字节的内存。**其它的没有区别,都可以是可以修改的或者共享的,都不拥有自己指向的值,它们的 lifetime 都不能超出目标的值。
0x04 小结
本篇文章介绍的知识点比较简单,主要了解下胖指针的概念就可以了。如果你了解 C++ ,那么你会发现,目前所介绍的引用与 C++ 非常像,但又有 Rust 的特点。下一篇文章将介绍 Rust 的新概念 lifetime 。