Rust中的&和ref使用解读
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>Rust中的&和ref使</li><ul class="second_class_ul"><li>1. & 和 ref 都是用来定义指针的</li><li>2. 只能用 & 定义指针的地方</li><li>3. 只能用 ref 定义指针的地方</li><li>4. 更多的试验</li><li>5. 指针变量的解引用</li></ul><li>总结</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>Rust中的&和ref使</h2><p class="maodian"></p><h3>1. & 和 ref 都是用来定义指针的</h3>
<p>废话少说,先看代码:</p>
<div class="jb51code"><pre class="brush:bash;">fn main() {
let mut a: i32 = 111;
let b = &a;
println!("{}", *b); //111
let ref c = a;
println!("{}", *c); //111
}</pre></div>
<p>而这结果一样,都是在声明一个指针。区别在那里?& 放在等号的右边, ref 放在等好的左边。</p>
<p>在看个例子,看看如何用指针修改变量:</p>
<div class="jb51code"><pre class="brush:bash;">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
}</pre></div>
<p>在代码行里,二者没有任何区别。但是,为什么弄出两个来呢?</p>
<p class="maodian"></p><h3>2. 只能用 & 定义指针的地方</h3>
<p>看一段代码:</p>
<div class="jb51code"><pre class="brush:bash;">fn foo(x: &mut i32) {
*x = 999;
}
fn main() {
let mut a: i32 = 111;
foo(&mut a);
println!("{}", a); // 999
}</pre></div>
<p>在函数传参的时候用到了 & 来表示传入参数栈的是一个可修改变量的地址。</p>
<p>下面我们修改一下,改成下面的代码:</p>
<div class="jb51code"><pre class="brush:bash;">fn foo(ref mut x: i32) {
*x = 999;
}
fn main() {
let mut a: i32 = 111;
foo(a);
println!("{}", a); // 111
}</pre></div>
<p>foo(a) 的调用语义是说,要把 a 的值复制到栈空间,因此,fn foo(ref mut x: i32) 中参数 x 引用的是栈上的数据。也就是说,不管函数的参数 x 如何声明,foo(a) 这种形式传入参数 a,都不可能修改变量 a 的值。我觉得这个规定是合理的,比 C++ 的引用的语义声明简单、合理多了。</p>
<p>我们再修改一下:</p>
<div class="jb51code"><pre class="brush:bash;">fn foo(ref x: &mut i32) {
**x = 999;
}
fn main() {
let mut a: i32 = 111;
foo(&mut a);
println!("{}", a); // 999
}</pre></div>
<p>这次又成功了。但是这个 **x 你不觉得麻烦吧?因此,在函数参数声明中,一般只用 & 来传入变量的地址。</p>
<p class="maodian"></p><h3>3. 只能用 ref 定义指针的地方</h3>
<p>看下面的代码:</p>
<div class="jb51code"><pre class="brush:bash;">fn main() {
let s = Some(String::from("Hello!"));
match s {
Some(t) => println!("t = {}", t),
_ => {}
}
println!("s = {}", s.unwrap());
}</pre></div>
<p>这个是无法编译的。因为 match s 语句中,已经把 s 的所有权给转移了,导致最后一条语句无法执行。</p>
<p>编译提示如下:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202502/202502261140111.png" /></p>
<p>编译期建议在模式匹配中把变量 t 改成 ref t,也就是说把 t 声明成指针即可解决问题。</p>
<p>修改后代码如下:</p>
<div class="jb51code"><pre class="brush:bash;">fn main() {
let s = Some(String::from("Hello!"));
match s {
Some(ref t) => println!("t = {}", t),
_ => {}
}
println!("s = {}", s.unwrap());
}</pre></div>
<p>因为在模式匹配代码块中,我们没有机会声明变量类型,只能用 ref 表示变量 t 是个指针。</p>
<p>我试了一下,不用 ref 的话,还有一个变通的方法,就是把 match s 改成 match &s。</p>
<p>代码如下:</p>
<div class="jb51code"><pre class="brush:bash;">fn main() {
let s = Some(String::from("Hello!"));
match &s {
Some(t) => println!("t = {}", t),
_ => {}
}
println!("s = {}", s.unwrap());
}</pre></div>
<p>这个时候 t 前面加不加 ref 结果都一样。因为 match 只是借用 s,所以不会影响 s 的生命周期。</p>
<p class="maodian"></p><h3>4. 更多的试验</h3>
<p>下面给出了一组代码,我们看看那些是合法的,那些是非法的。</p>
<div class="jb51code"><pre class="brush:bash;">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!
}</pre></div>
<p class="maodian"></p><h3>5. 指针变量的解引用</h3>
<p>看下面代码,道理我不多讲了,rust 会自动解多层嵌套引用,这个太方便了。</p>
<div class="jb51code"><pre class="brush:bash;">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
*/</pre></div>
<p class="maodian"></p><h2>总结</h2>
<p>以上为个人经验,希望能给大家一个参考,也希望大家多多支持琼殿技术社区。</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>Rust中的方法与关联函数使用解读</li><li>Rust中的模块系统之控制作用域与私有性详解</li><li>Rust之Rhai脚本编程的示例</li><li>Rust动态调用字符串定义的Rhai函数方式</li><li>Rust中的注释使用解读</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]