Rust学习笔记004:Rust的所有权机制

2024-01-01 18:40:02

内存相关的基础知识

不同语言的内存管理系统

在这里插入图片描述

栈和堆

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

堆和栈的使用

在这里插入图片描述
在这里插入图片描述

引出所有权方案

在这里插入图片描述

String类型

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Rust 的所有权机制

  • Rust 的所有权机制是一种内存管理系统,它允许在编译时通过所有权、借用和生命周期来确保内存安全,同时避免了垃圾回收的运行时开销。这些概念共同构成了 Rust 强大的所有权系统,通过编译时的检查来防止一类内存错误,如空悬指针和双重释放等。这种系统使得 Rust 具有内存安全性、线程安全性和零成本抽象等特性。

1. 所有权规则:

  • 每个值都有一个所有者: 在任意时刻,一个值只能有一个所有者。
  • 所有者离开作用域时,值会被销毁: 当拥有某个值的变量离开其作用域时,Rust 会自动调用该值的 Drop trait 中的 drop 方法来释放其占用的资源。

2. 移动(Move):

  • 当将一个值赋值给另一个变量时,所有权会从一个变量移动到另一个变量。这会导致原来的变量无法再使用该值。
let s1 = String::from("Hello");// 可以使用 from 函数从字符串字面值创建出 String 类型
let s2 = s1; // 所有权移动到变量 y
// println!("{}", x); // 这里编译器会报错,因为 x 不再拥有值的所有权
  • 隐含的一个设计原则: Rust 不会自动创建数据的深拷贝,就运行时性能而言,任何自动赋值的操作都是廉价的
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
相关:C++中,移动构造函数
  • 在C++中,移动构造函数(Move Constructor)是一种特殊的构造函数,用于在对象的所有权被转移(移动)时创建新对象。移动构造函数通常用于提高性能,特别是在涉及资源管理的情况下,比如使用动态内存分配的对象。

  • 移动构造函数是通过右值引用(Rvalue Reference)来实现的。在C++11及以后的标准中,可以通过定义移动构造函数来支持右值引用。移动构造函数的目标是在没有不必要的资源复制的情况下,将资源从一个对象“移动”到另一个对象,以提高效率。

  • 以下是一个简单的示例,演示了移动构造函数的用法:

#include <iostream>
#include <string>

class MyString {
public:
    // 移动构造函数
    MyString(MyString&& other) noexcept {
        std::cout << "Move constructor called" << std::endl;
        data = other.data;
        size = other.size;
        other.data = nullptr;
        other.size = 0;
    }

    // 构造函数
    MyString(const char* str) {
        size = strlen(str);
        data = new char[size + 1];
        strcpy(data, str);
    }

    // 析构函数
    ~MyString() {
        delete[] data;
    }

private:
    char* data;
    size_t size;
};

int main() {
    MyString str1("Hello");
    MyString str2 = std::move(str1); // 使用移动构造函数

    return 0;
}

在上述例子中,MyString 类具有移动构造函数。在 main 函数中,通过 std::movestr1 的所有权移动给了 str2。移动构造函数被调用后,str1 的资源被移动到 str2,同时 str1 的数据成员被置为 nullptr

注意:在移动构造函数中,我们通常将被移动对象的数据成员设置为安全状态,以避免资源重复释放。此外,通常需要在移动构造函数中使用 noexcept 来指定该函数不会抛出异常,以便在一些情况下提供更好的性能。

移动构造函数对于提高性能和避免不必要的资源复制是非常有用的,特别是在处理大型数据结构、动态分配内存等情况下。

3. 克隆(Clone,Copy):感觉类似c++的深拷贝构造

在这里插入图片描述

如果需要拷贝堆上数据而不是移动所有权,可以使用 clone 方法。但这会增加内存开销。

let x = String::from("Hello");
let y = x.clone(); // 创建了一个新的 String 对象,所有权移动到变量 y
println!("x: {}, y: {}", x, y); // 这样是合法的,因为 x 仍然有效

在这里插入图片描述

4. 借用(Borrowing):

通过借用,可以暂时获取对值的引用而不移动所有权。借用分为可变借用和不可变借用。

fn main() {
    let s1 = String::from("Hello");
    let len = calculate_length(&s1); // 通过引用借用 s1
    println!("Length of '{}' is: {}", s1, len); // 这里依然可以使用 s1,因为只是借用了引用

    // 可变借用
    let mut s2 = String::from("Rust");
    modify_string(&mut s2); // 通过可变引用借用 s2
    println!("Modified string: {}", s2);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

fn modify_string(s: &mut String) {
    s.push_str(" is awesome!");
}

5. 生命周期(Lifetime):

生命周期是用于描述引用有效范围的标记。Rust 的生命周期系统确保引用在其所引用的值有效的情况下才能被使用。

fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

fn main() {
    let s1 = String::from("Rust");
    let s2 = String::from("C");

    let result;
    {
        let s3 = String::from("Java");
        result = longest(s1.as_str(), s3.as_str());
    } // s3 的生命周期结束,result 引用的值仍然是有效的
    println!("The longest string is: {}", result);
}

在上述例子中,longest 函数使用了生命周期参数 'a,它表示返回的引用的生命周期与输入参数的生命周期一致。

所有权与函数

在 Rust 中,所有权系统与函数的参数传递和返回密切相关,它确保在函数调用过程中对所有权的正确管理。

在这里插入图片描述

1. 所有权传递:

当你将一个值传递给函数时,它的所有权会被转移给函数。这意味着在函数中你可以使用该值,但在函数返回后,调用者将无法再使用它。

在这里插入图片描述

fn take_ownership(s: String) {
    println!("Received: {}", s);
    // s 的所有权在这里结束,s 的内存将被释放
}

fn main() {
    let my_string = String::from("Hello");
    take_ownership(my_string); // my_string 的所有权被传递给 take_ownership 函数
    // println!("Value: {}", my_string); // 这里编译器会报错,因为 my_string 已经失去了所有权
}

2. 返回所有权:

函数也可以返回所有权,将所有权还给调用者。这可以通过在函数签名中指定返回类型为 -> 加上类型名。

在这里插入图片描述

fn return_ownership() -> String {
    let s = String::from("Returned");
    s // 所有权被返回给调用者
}

fn main() {
    let result = return_ownership(); // 调用 return_ownership 后,result 拥有了返回的所有权
    println!("Result: {}", result);
}

3. 引用传递:

  • 在所有权用完后,再返回回来?
    在这里插入图片描述

  • 如果你想在函数中使用值但不获取其所有权,可以使用引用传递。引用允许你在不移动所有权的情况下访问值。

在这里插入图片描述

fn borrow_value(s: &String) {
    println!("Borrowed: {}", s);
    // s 是引用,所有权不会在这里转移
}

fn main() {
    let my_string = String::from("Hello");
    borrow_value(&my_string); // 传递了 my_string 的引用
    println!("Value: {}", my_string); // 这是合法的,因为并未传递所有权
}

可变引用
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

字符串切片

在这里插入图片描述

文章来源:https://blog.csdn.net/ResumeProject/article/details/135317881
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。