每日见闻之Rust中的引用规则

规则一

所有权型变量的作用域是从它定义时开始到所属那层花括号结束。

代码示例

js 复制代码
fn main() {
    let mut a = 10u32;
    let b = &mut a;
    *b = 20;
    let c = &a;    
} //a的作用域到这里结束

规则二

引用型变量的作用域是从它定义起到它最后一次使用时结束。

代码示例

js 复制代码
fn main() {
    let mut a = 10u32;
    let b = &mut a;//b的作用域从这里开始
    *b = 20;//b的作用域到这里结束
    let c = &a;    
} //a的作用域到这里结束

规则三

一个所有权型变量的不可变引用可以同时存在多个,可以复制多份。

代码示例

js 复制代码
fn main() {
    let x = String::from("hello"); // 所有权变量 x
    
    // 创建多个不可变引用
    let r1 = &x; // 不可变引用 r1
    let r2 = &x; // 不可变引用 r2(同时存在,合法)
    let r3 = &x; // 不可变引用 r3(可创建任意多个)
    
    // 使用这些引用(均为只读)
    println!("r1: {}, r2: {}, r3: {}", r1, r2, r3);
    
    // 所有引用在作用域结束后自动释放,不影响原变量
    // x 的所有权仍在当前作用域内,未转移
}

规则四

一个所有权型变量的可变引用与不可变引用的作用域不能交叠,也可以说不能同时存在.

js 复制代码
fn main() {
    let mut a = 10u32;
    let c = &a;        // c作用域开始
    let b = &mut a; //b作用域开始
    *b = 20;//b作用域结束
  
    println!("{c}");//c作用域结束
}

不可变引用c的作用域包含了可变引用b的作用域。作用域重叠编译不通过

修正

js 复制代码
fn main() {
    let mut a = 10u32;
    let b = &mut a; //b作用域开始
    *b = 20;//b作用域结束
    let c = &a;        // c作用域开始
    println!("{c}");//c作用域结束
}

可变引用a的作用域跟不可变引用b作用域不重叠,编译通过。

规则五

某个时刻对某个所有权型变量只能存在一个可变引用,不能有超过一个可变借用同时存在,也可以说,对同一个所有权型变量的可变借用之间的作用域不能交叠

示例

js 复制代码
fn main() {
    let mut a = 10u32;
    let b = &mut a; //b的作用域开始
    *b = 20;
    let d = &mut a; //d的作用开始 结束
    
    println!("{b}");//b的作用域结束
}

可变引用b,d作用域重叠,编译不通过。简单说就是声明新的可变引用的时候,在他之前声明的可变引用不可以再使用。

规则六

在有借用存在的情况下,不能通过原所有权型变量对值进行更新。当借用完成后(借用的作用域结束后),物归原主,又可以使用所有权型变量对值做更新操作了

示例

js 复制代码
fn main() {
    let mut a = 10u32;
    let r1 = &mut a;
    a = 20;
    
    println!("{r1}");
}

在有可变引用r1的情况下,所有权变量修改值,编译不通过。简单说就是想要操作所有权变量,就要保证之前的所有引用在所有权变量操作之后不会再用到。

规则七

可变引用的再赋值,会执行移动操作。赋值后,原来的那个可变引用变量就不能用了。这有点类似于所有权的转移,因此一个所有权型变量的可变引用也具有所有权特征,它可以被理解为那个所有权变量的独家代理,具有排它性

示例

js 复制代码
fn main() {
    let mut a = 10u32;
    let r1 = &mut a;
    let r2 = r1;
     println!("{r1}");
    println!("{r2}");    // 打印r2
}

可变引入r1 赋值给r2之后,这里执行的是move操作,也就是说r1不能再使用了,打印r1编译不通过。

后记

用引用改进函数的定义

传递不可变引用到函数

js 复制代码
fn foo(s: &String) {
    println!("in fn foo: {s}");
}

fn main() {
    let s1 = String::from("I am a superman.");
    foo(&s1);    // 注意这里传的是字符串的引用 &s1
    println!("{s1}");    // 这里可以打印s1的值了
}

传递可变引用到函数

js 复制代码
fn foo(s: &mut String) {
    s.push_str(" You are batman.");
}

fn main() {
    let mut s1 = String::from("I am a superman.");
    println!("{s1}");
    foo(&mut s1);    // 注意这里传的是字符串的可变引用 &mut s1
    println!("{s1}");
}

最后大白话总结:只有不可变引用的时候,随便用。如果涉及到所有权变量修改值或者可变引用,那就要保证在声明新的引用的时候或者使用所有权变量修改值的时候,之前声明的引用不可以再用。

相关推荐
智慧物业老杨2 小时前
物业日常巡查的数智化重构:从“打卡式巡检“到“闭环式风控“
android·java·人工智能·系统架构·rxjava
alexhilton8 小时前
藏在设备上的秘密,终究藏不住
android·kotlin·android jetpack
hai_android14 小时前
Kotlin / Android 常用函数使用示例手册
android·java·kotlin
hai_android15 小时前
Android MeasureSpec 详解
android·java·kotlin
又见情义17 小时前
RK3568 Android 13 本地 U 盘 OTA 升级实战:基于 RKUpdateService 的完整流程
android
Godikov18 小时前
Android10后台弹窗与APK自动更新
android
又见情义19 小时前
RK3568 Android 13 USB OTG/Host 切换调试经验分享
android
终端安全笔记20 小时前
iOS 27 之后「策略空转」:设备升级不报错,但旧策略不再管它
android·网络·安全·ios·智能手机
JMchen21 小时前
实战案例:实现120fps流畅的渐变进度条
android·kotlin·canvas
敲代码的瓦龙21 小时前
Jetpack?DataBinding!!!
android·java·开发语言·mysql·android-studio