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 };
}
相关推荐
程序猿追3 小时前
kotlin-retry 接入鸿蒙后,怎样确认它真的会停止重试
开发语言·kotlin·harmonyos
我是人✓3 小时前
Java文件上传三方式对比:从传统到SpringBoot
java·开发语言
智能直播3 小时前
网络RTMP拉流不卡顿、声音不同步,一文讲透缓冲区、时间戳
开发语言·ffmpeg·php·音视频·视频编解码
墨云安全4 小时前
内网资产无法暴露到公网,怎么做安全检测?2026 年内网渗透测试的落地路径
开发语言·安全·网络安全·渗透测试·php
摇滚侠4 小时前
《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 9 章 机器码与汇编语言 阅读笔记 9
java·开发语言·笔记
Cc.Y4 小时前
Java零基础入门:深入Java核心:文件IO与序列化全解
java·开发语言·python
jimy16 小时前
c++的 Type Traits (一)
开发语言·c++
Web极客码7 小时前
Python Ollama 本地 Agent 入门与订单查询实践
开发语言·jvm·python
kimnoic7 小时前
PHP 8.2的DNF类型怎么用才规范
开发语言·php
_Evan_Yao7 小时前
前后端联调怎么排查问题?从 Network 到后端日志,一步步定位错误
开发语言·后端·团队开发·需求分析