Rust中的&和ref使用解读

 更新时间:2025年02月26日 11:39:28   作者:许野平  
在Rust中,`&`和`ref`都可以用来定义指针,但它们的使用位置不同,`&`通常放在等号右边,而`ref`放在左边,`&`主要用于函数参数和模式匹配中,而`ref`主要用于模式匹配中,Rust通过`&`和`ref`提供了灵活的指针操作,使得代码更加安全和高效

Rust中的&和ref使

1. & 和 ref 都是用来定义指针的

废话少说,先看代码:

fn main() {
    let mut a: i32 = 111;

    let b = &a;
    println!("{}", *b); //111

    let ref c = a;
    println!("{}", *c); //111
}

而这结果一样,都是在声明一个指针。区别在那里?& 放在等号的右边, ref 放在等好的左边。

在看个例子,看看如何用指针修改变量:

fn main() {
    let mut a: i32 = 111;

    let b = &mut a; // 指针 b 本身是不可以修改的,但它指向的内容是可以修改的。
    *b = 222;
    println!("{}", a); // 222

    let ref mut c = a; // 指针 c 本身是不可以修改的,但它指向的内容是可以修改的。
    *c = 333;
    println!("{}", a); //333
}

在代码行里,二者没有任何区别。但是,为什么弄出两个来呢?

2. 只能用 & 定义指针的地方

看一段代码:

fn foo(x: &mut i32) {
    *x = 999;
}
fn main() {
    let mut a: i32 = 111;
    foo(&mut a);
    println!("{}", a); // 999
}

在函数传参的时候用到了 & 来表示传入参数栈的是一个可修改变量的地址。

下面我们修改一下,改成下面的代码:

fn foo(ref mut x: i32) {
    *x = 999;
}
fn main() {
    let mut a: i32 = 111;
    foo(a);
    println!("{}", a); // 111
}

foo(a) 的调用语义是说,要把 a 的值复制到栈空间,因此,fn foo(ref mut x: i32) 中参数 x 引用的是栈上的数据。也就是说,不管函数的参数 x 如何声明,foo(a) 这种形式传入参数 a,都不可能修改变量 a 的值。我觉得这个规定是合理的,比 C++ 的引用的语义声明简单、合理多了。

我们再修改一下:

fn foo(ref x: &mut i32) {
    **x = 999;
}
fn main() {
    let mut a: i32 = 111;
    foo(&mut a);
    println!("{}", a); // 999
}

这次又成功了。但是这个 **x 你不觉得麻烦吧?因此,在函数参数声明中,一般只用 & 来传入变量的地址。

3. 只能用 ref 定义指针的地方

看下面的代码:

fn main() {
    let s = Some(String::from("Hello!"));
    match s {
        Some(t) => println!("t = {}", t),
        _ => {}
    }
    println!("s = {}", s.unwrap());
}

这个是无法编译的。因为 match s 语句中,已经把 s 的所有权给转移了,导致最后一条语句无法执行。

编译提示如下:

编译期建议在模式匹配中把变量 t 改成 ref t,也就是说把 t 声明成指针即可解决问题。

修改后代码如下:

fn main() {
    let s = Some(String::from("Hello!"));
    match s {
        Some(ref t) => println!("t = {}", t),
        _ => {}
    }
    println!("s = {}", s.unwrap());
}

因为在模式匹配代码块中,我们没有机会声明变量类型,只能用 ref 表示变量 t 是个指针。

我试了一下,不用 ref 的话,还有一个变通的方法,就是把 match s 改成 match &s。

代码如下:

fn main() {
    let s = Some(String::from("Hello!"));
    match &s {
        Some(t) => println!("t = {}", t),
        _ => {}
    }
    println!("s = {}", s.unwrap());
}

这个时候 t 前面加不加 ref 结果都一样。因为 match 只是借用 s,所以不会影响 s 的生命周期。

4. 更多的试验

下面给出了一组代码,我们看看那些是合法的,那些是非法的。

fn main() {
    let v = 123;

    let x: &i32 = &v; 					// OK!
    let x: &i32 = &(123 + 456); 		// OK!
    if let Some(x:&i32) = Some(&123); 	// Error!

    let ref x: i32 = v;					// OK!
    let ref x: i32 = 123 + 456;			// OK!
    if let Some(ref x) = Some(123) {}	// OK!
}

5. 指针变量的解引用

看下面代码,道理我不多讲了,rust 会自动解多层嵌套引用,这个太方便了。

fn main() {
    let a: &i32 = &123;
    let b: &&i32 = &a;
    let c: &&&i32 = &b;

    println!("a = {}, b = {}, c = {}", a, b, c);
    println!("*a = {}, **b = {}, ***c = {}", *a, **b, ***c);
}

/* output
a = 123, b = 123, c = 123
*a = 123, **b = 123, ***c = 123
*/

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 详解Rust调用tree-sitter支持自定义语言解析

    详解Rust调用tree-sitter支持自定义语言解析

    使用Rust语言结合tree-sitter库解析自定义语言需要定义语法、生成C解析器,并在Rust项目中集成,具体步骤包括创建grammar.js定义语法,使用tree-sitter-cli工具生成C解析器,以及在Rust项目中编写代码调用解析器,这一过程涉及到对tree-sitter的深入理解和Rust语言的应用技巧
    2024-09-09
  • 详解Rust 生命周期符号使用的方法和规律

    详解Rust 生命周期符号使用的方法和规律

    生命周期是 Rust 中处理引用和所有权的关键概念,通过正确使用生命周期符号和遵循相关规律,你可以编写出安全、高效的 Rust 代码,这篇文章主要介绍了Rust 生命周期符号使用的方法和规律,需要的朋友可以参考下
    2024-03-03
  • C和Java没那么香了,Serverless时代Rust即将称王?

    C和Java没那么香了,Serverless时代Rust即将称王?

    Serverless Computing,即”无服务器计算”,其实这一概念在刚刚提出的时候并没有获得太多的关注,直到2014年AWS Lambda这一里程碑式的产品出现。Serverless算是正式走进了云计算的舞台
    2021-06-06
  • Rust使用kind进行异常处理(错误的分类与传递)

    Rust使用kind进行异常处理(错误的分类与传递)

    Rust 有一套独特的处理异常情况的机制,它并不像其它语言中的 try 机制那样简单,这篇文章主要介绍了Rust指南错误的分类与传递以及使用kind进行异常处理,需要的朋友可以参考下
    2022-09-09
  • Rust使用csv crate构建CSV文件读取器的全过程

    Rust使用csv crate构建CSV文件读取器的全过程

    这篇文章主要学习如何基于Rust使用csv这个crate构建一个CSV文件读取器的过程,学习了csv相关的用法以及一些往期学过的crate的复习,兼顾了实用性和Rust的学习,需要的朋友可以参考下
    2024-05-05
  • Rust中的Drop特性之解读自动化资源清理的魔法

    Rust中的Drop特性之解读自动化资源清理的魔法

    Rust通过Drop特性实现了自动清理机制,确保资源在对象超出作用域时自动释放,避免了手动管理资源时可能出现的内存泄漏或双重释放问题,智能指针如Box、Rc和RefCell都依赖于Drop来管理资源,提供了灵活且安全的资源管理方案
    2025-02-02
  • Rust重载运算符之复数四则运算的实现

    Rust重载运算符之复数四则运算的实现

    这篇文章主要为大家详细介绍了Rust如何实现复数以及复数的四则运算,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-08-08
  • rust引用和借用的使用小结

    rust引用和借用的使用小结

    在rust中,引用的语法非常简单。通过&来取引用,通过*来解引用,这篇文章主要介绍了rust引用和借用的使用小结,总的来说,借用规则,同一时刻,你只能拥有要么一个可变引用, 要么任意多个不可变引用,具体内容详情跟随小编一起看看吧
    2023-01-01
  • Rust语言从入门到精通系列之Iterator迭代器深入详解

    Rust语言从入门到精通系列之Iterator迭代器深入详解

    这篇文章主要为大家介绍了Rust语言从入门到精通系列之Iterator迭代器深入详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Rust使用Channel实现跨线程传递数据

    Rust使用Channel实现跨线程传递数据

    消息传递是一种很流行且能保证安全并发的技术,Rust也提供了一种基于消息传递的并发方式,在rust里使用标准库提供的Channel来实现,下面我们就来学习一下如何使用Channel实现跨线程传递数据吧
    2023-12-12

最新评论