Rust的所有权(Owenership)

所有权是Rust最核心的概念。他有三条基本规则:

  1. Rust中每一个值都有一个被称为其所有者(owner)的变量。
  2. 值在任意时刻有且只有一个所有者。
  3. 当所有者(变量)离开作用域,这个值将被丢弃。
rust 复制代码
fn main() {
    let s1=String::from("hello");
    let s2=s1;
    // println!("{}",s1); 错误s1已无效
    println!("{}",s2);

    let s3=s2.clone();
    println!("s2:{},s3:{}",s2,s3);
}

所有权规则

rust 复制代码
fn main() {
    // 规则1:每个值都有一个所有者
    let s=String::from("hello"); // s是hello的所有者
    //规则2:值在任一时刻有且只有一个所有者
    let s2=s; //s的所有权移动到s2
    // println!("{}",s1); 错误s1已无效
    println!("{}",s2);
    // 规则3:所有者离开作用域,值被丢弃
    {
        let s3=String::from("world");
        // s3在这里有效
    } // s3离开作用域,值被自动释放
    let s4=String::from("test");
    takes_ownership(s4); // s4的所有权转移到函数
    //println!("{}",s4); //错误! s4已无效
}
fn takes_ownership(some_string: String) {
    println!("{}", some_string);
} // some_string 离开作用域,值被丢弃

移动

当将值赋给另一个变量或传递给函数时,会发生所有权的移动。

对于存储在堆上的数据(如 String、Vec),赋值会移动所有权,原变量失效。

对于实现了Copy trait的类型(如整数),会复制而不是移动。

rust 复制代码
fn main() {
    let s1=String::from("hello");
    let s2=s1; // s1的所有权移动到s2
    // println!("{}",s1); 错误 s1 已无效

    // 整数实现了Copy,会复制
    let x=5;
    let y=x;
    println!("{} {}",x,y);

    let s3=String::from("world");
    take_string(s3); //s3的所有权移动到函数
    //println!("{}",s3); 错误

    //返回值也会转移所有权
    let s4=gives_ownership();
    println!("{}", s4);

}
fn take_string(s:String){
    println!("{}", s);
} //离开作用域,值被丢弃

fn gives_ownership()->String{
    let some_string = String::from("hello");
    some_string // 所有权返回给调用者
}

克隆

当需要复制堆上的数据时,可以使用clone方法。clone会创建数据的深拷贝,这意味着会复制堆上的所有数据。这可能会很昂贵,所以要谨慎使用。

rust 复制代码
fn main() {
   let s1=String::from("hello");
    let s2=s1.clone(); //深拷贝,s1和s2都有效
    println!("s1:{},s2:{}", s1,s2);
    
    // 对于实现了Clone traint 的类型
    let v1=vec![1,2,3];
    let v2=v1.clone();
    println!("v1: {:?},v2: {:?}",v1,v2);
    
    // 结构体也可以实现clone
    #[derive(Clone)]
    struct Point{
        x: i32,
        y: i32,
    }
    let p1=Point{x:0,y:0};
    let p2=p1.clone();
    println!("p1: ({},{}),p2({},{})",p1.x,p1.y,p2.x,p2.y);
    
    let large_string=String::from("a very long string...");
    let copy=large_string.clone(); //复制所有数据
    
}

引用与借用

rust 复制代码
fn main() {
    let s1= String::from("hello");
    let len=calculate_lenth(&s1); // &s1创建引用
    println!("The length of '{}' is {}.", s1, len); // s1仍然有效
    // 引用默认不可变
    let s2= String::from("world");
    // change(&s2); 错误!不能通过不可变引用修改值
}
fn calculate_lenth(s: &String)-> usize{
    s.len()
} // 离开作用域,但因为它只是引用不会丢弃值

// 这个函数会编译错误
// fn change(some_string: &String){
//  some_string.push_str(",world");
// }

可变引用

可变引用允许修改借用的值。使用&mut创建可变引用。

可变引用有一个重要限制:在特定作用域中,对特定数据只能有一个可变引用。这防止了数据竞争。

rust 复制代码
fn main() {
    let mut s=String::from("hello");

    // 创建可变引用
    change(&mut s);
    println!("{}",s);

    //可变引用的限制
    let mut s2=String::from("test");
    let r1=&mut s2;
    // let r2=&mut s2; //错误!不能有多个引用
    println!("{}",r1);

    // 但是可以有多个不可变引用
    let s3=String::from("hello");
    let r1=&s3;
    let r2=&s3;
    println!("{}, {}",r1,r2);

    //不能同时有可变和不可变引用
    let mut s4=String::from("hello");
    let r1=&s4;
    //let r2=&mut s4;
    println!("{}",r1);
}
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

悬垂引用

悬垂引用是指指向已被释放内存的引用。在Rust中,编译器会确保引用永远不会比它指向的数据活得更久。Rust的借用检查器会检查所有引用,确保它们都是有效的。

rust 复制代码
fn main() {
    // 这个函数会编译错误,因为返回了悬垂引用
    // fn dangle() -> &String {
    //      let s = String::from("hello");
    //     &s 错误! s离开作用域后会被丢弃
    // }
    
    //正确的做法:返回所有权
    let s=no_dangle();
    println!("{}", s);
}
fn no_dangle()->String{
    let s = String::from("hello");
    s
}

// Rust的借用检查器会组织这样的代码:
//{
//         let r;
//         {
//                 let x=5;
//                  r = &x; //错误!x的生命周期不够长
        // }
//             println!("{}",r); // r指向x已经不存在
//}

生命周期基础

生命周期是Rust中引用有效的作用域。生命周期确保引用在使用时是有效的。大多情况下生命周期是隐式的,编译器可以推断。但有时需要显式标注生命周期参数。

rust 复制代码
fn main() {
    let string1=String::from("long string is long");

    {
        let string2=String::from("xyz");
        let result= longest(string1.as_str(), string2.as_str());
        println!("The longest string is {}", result);
    }
fn longest<'a>(x: &'a str,y: &'a str)->&'a str{
    if x.len()>y.len(){x}
    else{y}
}
}
rust 复制代码
// 生命周期标注 'a表示
// - 参数x和y的引用至少和'a一样长
// - 返回的引用也至少和'a一样长
// 结构体中的生命周期
use std::ops::Bound::Included;

struct ImportantExcerpt<'a> {
    part: &'a str,
}
fn main() {
    let novel=String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    let i=ImportantExcerpt { part: first_sentence };
}
相关推荐
独自破碎E2 小时前
说说Java中的JIT
java·开发语言
齐鲁大虾2 小时前
如何通过Java调取打印机打印图片和文本
java·开发语言·python
Remember_9932 小时前
【数据结构】初识 Java 集合框架:概念、价值与底层原理
java·c语言·开发语言·数据结构·c++·算法·游戏
hqwest2 小时前
码上通QT实战33--监控页面14-刻度盘旋转
开发语言·qt·qdial·qlcdnumber·modbus功能码06
源代码•宸2 小时前
Golang原理剖析(channel源码分析)
开发语言·后端·golang·select·channel·hchan·sudog
liuyunshengsir2 小时前
golang Gin 框架下的大数据量 CSV 流式下载
开发语言·golang·gin
BlockChain8882 小时前
MPC 钱包实战(三):Rust MPC Node + Java 调度层 + ETH 实际转账(可运行)
java·开发语言·rust
吉吉612 小时前
在 Windows 和 Linux 的 VSCode 中配置 PHP Debug
开发语言·php
蜜汁小强2 小时前
macOS 上升级到 python 3.12
开发语言·python·macos