首页 > 其他分享 >【rCore OS 开源操作系统】Rust 智能指针

【rCore OS 开源操作系统】Rust 智能指针

时间:2024-10-19 15:19:25浏览次数:8  
标签:count Cow rCore sun let Rc println OS Rust

前置知识点

何为“智能”

在 Rust 中,“智能指针”是指那些实现了特定智能行为的指针类型。这些智能行为通常包括内存管理生命周期跟踪以及所有权转移等

常见智能指针

Box

Box<T> 是 Rust 中最简单的智能指针类型之一,它用于堆分配的内存。Box<T> 允许你在堆上分配类型 T 的对象,并且它实现了 Deref trait,使得你可以像操作栈上的对象一样操作堆上的对象。

说人话:就是用这个,可以把本来该放到栈上的内容,放到堆上

let boxed_num = Box::new(5); // 堆上放一个 5,然后 boxed_num 就是引用
println!("Boxed number is: {}", *boxed_num); // 所以这里要解引用
Rc 和 Arc

前者后者的区别就是,前者用于一般场景,后者是 Aotomic 所以用于多线程常见

Rc<T>(Reference Counted)用于共享所有权的情况,即多个地方需要访问同一个数据。它通过引用计数来跟踪有多少个 Rc 指向同一个数据。

Arc<T>Atomic Rc)与 Rc<T> 类似,但它是线程安全的,适用于多线程环境中共享数据。

说人话就是,一个堆上的数据,要被多处共享,但是 rust 默认情况下有个所有权机制,直接共享就有问题要报错,要用这个包一下才能共享

let rc_str = Rc::new(String::from("hello"));
let clone = Rc::clone(&rc_str);

println!("Original string: {}", rc_str);
println!("Cloned string: {}", clone);

Arc<T> 也是同理,此处不再赘述。后面在练习题中看具体使用。

RefCell

RefCell<T> 提供了对数据的可变借用,这对于在单线程中打破 Rust 的借用规则很有用。RefCell<T> 使用运行时检查来保证互斥性。
RefCell<T> 通常用于需要在运行时决定借用关系的情况,例如在递归数据结构中。

如果你是做 Java 的,一定很熟悉多线程加法!对,也就是那么个道理。
具体的代码放到题目中体现,看看 Rust 如何用 RefCell<T>实现

Cow

Cow<T>Clone and Write 的缩写。

作者真是个取名天才, 这谁能想得到啊…

Cow 是一个智能指针类型,它允许你在不进行克隆的情况下处理字符串或其他数据类型。具体来说,Cow 可以在以下两种情况下工作:

  • 借用(Borrow):当数据不需要被修改时,Cow 只是一个借用。
  • 克隆(Clone):当数据需要被修改时,Cow 会克隆数据并提供一个可变版本。

我称之为,“懒克隆”
代码看后面的题目。

let original_str = "Hello, world!";

// 借用字符串
let borrowed_str = Cow::from(original_str);
println!("Borrowed str: {:?}", borrowed_str);

// 修改字符串
let modified_str = Cow::from("Hello, Rustaceans!");
println!("Modified str: {:?}", modified_str);

// 如果需要修改,`Cow` 会克隆字符串
let mut mutable_str = Cow::from("Hello, Rust!");
mutable_str.to_mut().push_str("aceans");
println!("Mutable str after modification: {:?}", mutable_str);

Weak

Weak<T>Rc<T>Arc<T> 结合使用,当不再需要强引用时,可以使用 Weak<T> 来保持对对象的弱引用,这样即使有弱引用存在,对象也可以被垃圾回收。
这对于实现某些设计模式(如观察者模式)很有用。

感觉也不怎么用得到这个…学前端学了这么久唯一的感受就是在 Vue 的实现里有用过,其他还真是一概不知…

NonNull

NonNull<T> 是一个非空指针,它并不包含任何智能行为,但是它可以用来表示未经过 Rust 内存管理系统管理的裸指针。
这对于与 C/C++ 代码交互或操作底层内存时很有用。

(挠头)…感觉也没什么用

练习题

Arc

题目

来吧,实现一些多线程加法。

// arc1.rs
//
// In this exercise, we are given a Vec of u32 called "numbers" with values
// ranging from 0 to 99 -- [ 0, 1, 2, ..., 98, 99 ] We would like to use this
// set of numbers within 8 different threads simultaneously. Each thread is
// going to get the sum of every eighth value, with an offset.
//
// The first thread (offset 0), will sum 0, 8, 16, ...
// The second thread (offset 1), will sum 1, 9, 17, ...
// The third thread (offset 2), will sum 2, 10, 18, ...
// ...
// The eighth thread (offset 7), will sum 7, 15, 23, ...
//
// Because we are using threads, our values need to be thread-safe.  Therefore,
// we are using Arc.  We need to make a change in each of the two TODOs.
//
// Make this code compile by filling in a value for `shared_numbers` where the
// first TODO comment is, and create an initial binding for `child_numbers`
// where the second TODO comment is. Try not to create any copies of the
// `numbers` Vec!
//
// Execute `rustlings hint arc1` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

#![forbid(unused_imports)] // Do not change this, (or the next) line.
use std::sync::Arc;
use std::thread;

fn main() {
    let numbers: Vec<_> = (0..100u32).collect();
    let shared_numbers = // TODO
    let mut joinhandles = Vec::new();

    for offset in 0..8 {
        let child_numbers = // TODO
        joinhandles.push(thread::spawn(move || {
            let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();
            println!("Sum of offset {} is {}", offset, sum);
        }));
    }
    for handle in joinhandles.into_iter() {
        handle.join().unwrap();
    }
}

题解
#![forbid(unused_imports)] // Do not change this, (or the next) line.
use std::sync::Arc;
use std::thread;

fn main() {
    // collect 会把 迭代器 转为 Vec<_> 的 vec(是的,根据类型推导
    let numbers: Vec<_> = (0..100u32).collect();
    // arc 和 rc 都是支持计数的,但是 arc 是原子的、线程安全的,而 rc 不是。
    let shared_numbers = Arc::new(numbers);
    let mut joinhandles = Vec::new();

    for offset in 0..8 {
        // 这里为啥不直接用 shared_numbers 呢?
        // 是因为借用检查:Rust 的借用检查器(borrow checker)会阻止你直接将 shared_numbers 传入多个线程中的闭包,
        // 因为这会导致多个线程同时访问同一个引用——这在 Rust 中是不允许的
        let child_numbers = Arc::clone(&shared_numbers);
        //在 Rust 中,闭包默认捕获外部作用域中的变量的方式有两种: 借用(默认情况,只读这些变量) 和 移动(可以改这些变量)
        // Rust 不允许多个线程同时访问同一个引用
        // 这里由于是使用基本数据类型——天然支持 copy trait,所以可以直接通过移动获取一份副本
        joinhandles.push(thread::spawn(move || {
            // iter() 返回的是 &u32,filter 一般是都是 &xxx 引用,所以二者叠加就是 &&n
            let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();
            println!("Sum of offset {} is {}", offset, sum);
        }));
    }
    for handle in joinhandles.into_iter() {
        handle.join().unwrap();
    }
}

Rc

题目

主要是引用计数的知识点。
RC:clone 去克隆一个引用。
场景说实话有点难,好几次没记住行星名字…反复对照了好几次…

// rc1.rs
//
// In this exercise, we want to express the concept of multiple owners via the
// Rc<T> type. This is a model of our solar system - there is a Sun type and
// multiple Planets. The Planets take ownership of the sun, indicating that they
// revolve around the sun.
//
// Make this code compile by using the proper Rc primitives to express that the
// sun has multiple owners.
//
// Execute `rustlings hint rc1` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

use std::rc::Rc;

#[derive(Debug)]
struct Sun {}

#[derive(Debug)]
enum Planet {
    Mercury(Rc<Sun>),
    Venus(Rc<Sun>),
    Earth(Rc<Sun>),
    Mars(Rc<Sun>),
    Jupiter(Rc<Sun>),
    Saturn(Rc<Sun>),
    Uranus(Rc<Sun>),
    Neptune(Rc<Sun>),
}

impl Planet {
    fn details(&self) {
        println!("Hi from {:?}!", self)
    }
}

fn main() {
    let sun = Rc::new(Sun {});
    println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference

    let mercury = Planet::Mercury(Rc::clone(&sun));
    println!("reference count = {}", Rc::strong_count(&sun)); // 2 references
    mercury.details();

    let venus = Planet::Venus(Rc::clone(&sun));
    println!("reference count = {}", Rc::strong_count(&sun)); // 3 references
    venus.details();

    let earth = Planet::Earth(Rc::clone(&sun));
    println!("reference count = {}", Rc::strong_count(&sun)); // 4 references
    earth.details();

    let mars = Planet::Mars(Rc::clone(&sun));
    println!("reference count = {}", Rc::strong_count(&sun)); // 5 references
    mars.details();

    let jupiter = Planet::Jupiter(Rc::clone(&sun));
    println!("reference count = {}", Rc::strong_count(&sun)); // 6 references
    jupiter.details();

    // TODO
    let saturn = Planet::Saturn(Rc::new(Sun {}));
    println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
    saturn.details();

    // TODO
    let uranus = Planet::Uranus(Rc::new(Sun {}));
    println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
    uranus.details();

    // TODO
    let neptune = Planet::Neptune(Rc::new(Sun {}));
    println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
    neptune.details();

    assert_eq!(Rc::strong_count(&sun), 9);

    drop(neptune);
    println!("reference count = {}", Rc::strong_count(&sun)); // 8 references

    drop(uranus);
    println!("reference count = {}", Rc::strong_count(&sun)); // 7 references

    drop(saturn);
    println!("reference count = {}", Rc::strong_count(&sun)); // 6 references

    drop(jupiter);
    println!("reference count = {}", Rc::strong_count(&sun)); // 5 references

    drop(mars);
    println!("reference count = {}", Rc::strong_count(&sun)); // 4 references

    // TODO
    println!("reference count = {}", Rc::strong_count(&sun)); // 3 references

    // TODO
    println!("reference count = {}", Rc::strong_count(&sun)); // 2 references

    // TODO
    println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference

    assert_eq!(Rc::strong_count(&sun), 1);
}

题解

修改也就是复用Sun引用就好,而不是新建一个引用。
这里还涉及到一个全局函数 drop, 用来手动析构,同时可以让Rc的引用减一。


fn main() {
    let sun = Rc::new(Sun {});
    println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference	
	// 省略 部分 代码!!!!
	// 省略 部分 代码!!!!
	// 省略 部分 代码!!!!


    // TODO
    let saturn = Planet::Saturn(Rc::clone(&sun));
    println!("reference count = {}", Rc::strong_count(&sun)); // 7 references
    saturn.details();

    // TODO
    let uranus = Planet::Uranus(Rc::clone(&sun));
    println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
    uranus.details();

    // TODO
    let neptune = Planet::Neptune(Rc::clone(&sun));
    println!("reference count = {}", Rc::strong_count(&sun)); // 9 references
    neptune.details();

    assert_eq!(Rc::strong_count(&sun), 9);

    drop(neptune);
    println!("reference count = {}", Rc::strong_count(&sun)); // 8 references
	// 省略 部分 代码!!!!
	// 省略 部分 代码!!!!
	// 省略 部分 代码!!!!
 
    drop(mars);
    println!("reference count = {}", Rc::strong_count(&sun)); // 4 references

    // TODO
    drop(mercury);
    println!("reference count = {}", Rc::strong_count(&sun)); // 3 references

    // TODO
    drop(venus);
    println!("reference count = {}", Rc::strong_count(&sun)); // 2 references

    // TODO
    drop(earth);
    println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference

    assert_eq!(Rc::strong_count(&sun), 1);
}

Box

题目

这个题目好就好在,让我知道了什么场景下会用到 Box<T>

是啊,什么场景下不能放栈上而要放到堆上?
什么,你说数据结构超级大的时候?
那这会不会是设计不合理呢,什么栈上数据结构能 辣么大?

// box1.rs
//
// At compile time, Rust needs to know how much space a type takes up. This
// becomes problematic for recursive types, where a value can have as part of
// itself another value of the same type. To get around the issue, we can use a
// `Box` - a smart pointer used to store data on the heap, which also allows us
// to wrap a recursive type.
//
// The recursive type we're implementing in this exercise is the `cons list` - a
// data structure frequently found in functional programming languages. Each
// item in a cons list contains two elements: the value of the current item and
// the next item. The last item is a value called `Nil`.
//
// Step 1: use a `Box` in the enum definition to make the code compile
// Step 2: create both empty and non-empty cons lists by replacing `todo!()`
//
// Note: the tests should not be changed
//
// Execute `rustlings hint box1` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

#[derive(PartialEq, Debug)]
pub enum List {
    Cons(i32, List),
    Nil,
}

fn main() {
    println!("This is an empty cons list: {:?}", create_empty_list());
    println!(
        "This is a non-empty cons list: {:?}",
        create_non_empty_list()
    );
}

pub fn create_empty_list() -> List {
    todo!()
}

pub fn create_non_empty_list() -> List {
    todo!()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_create_empty_list() {
        assert_eq!(List::Nil, create_empty_list())
    }

    #[test]
    fn test_create_non_empty_list() {
        assert_ne!(create_empty_list(), create_non_empty_list())
    }
}

题解

我的了个 Holy Gosh 啊…竟然是递归数据结构…确实没想到…
不过这样说来,rust 竟然是把 enum 这类东西放栈上,有点离谱…

// Box 是一种智能指针,让我们直接在堆上分配内存,绕过栈内存的大小限制(放到堆上
// 比如下面这个 List,可以看到它的 Cons 成员中还有 List,这就产生了递归,Rust 会认为在栈上无法存下,所以需要使用 Box 改到堆上
#[derive(PartialEq, Debug)]
pub enum List {
    Cons(i32, Box<List>),
    Nil,
}

fn main() {
    println!("This is an empty cons list: {:?}", create_empty_list());
    println!(
        "This is a non-empty cons list: {:?}",
        create_non_empty_list()
    );
}

pub fn create_empty_list() -> List {
    List::Nil
}

pub fn create_non_empty_list() -> List {
    List::Cons(123123, Box::new(List::Nil))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_create_empty_list() {
        assert_eq!(List::Nil, create_empty_list())
    }

    #[test]
    fn test_create_non_empty_list() {
        assert_ne!(create_empty_list(), create_non_empty_list())
    }
}

Cow

题目
// cow1.rs
//
// This exercise explores the Cow, or Clone-On-Write type. Cow is a
// clone-on-write smart pointer. It can enclose and provide immutable access to
// borrowed data, and clone the data lazily when mutation or ownership is
// required. The type is designed to work with general borrowed data via the
// Borrow trait.
//
// This exercise is meant to show you what to expect when passing data to Cow.
// Fix the unit tests by checking for Cow::Owned(_) and Cow::Borrowed(_) at the
// TODO markers.
//
// Execute `rustlings hint cow1` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

use std::borrow::Cow;

fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> {
    for i in 0..input.len() {
        let v = input[i];
        if v < 0 {
            // Clones into a vector if not already owned.
            input.to_mut()[i] = -v;
        }
    }
    input
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn reference_mutation() -> Result<(), &'static str> {
        // Clone occurs because `input` needs to be mutated.
        let slice = [-1, 0, 1];
        let mut input = Cow::from(&slice[..]);
        match abs_all(&mut input) {
            Cow::Owned(_) => Ok(()),
            _ => Err("Expected owned value"),
        }
    }

    #[test]
    fn reference_no_mutation() -> Result<(), &'static str> {
        // No clone occurs because `input` doesn't need to be mutated.
        let slice = [0, 1, 2];
        let mut input = Cow::from(&slice[..]);
        match abs_all(&mut input) {
            // TODO
        }
    }

    #[test]
    fn owned_no_mutation() -> Result<(), &'static str> {
        // We can also pass `slice` without `&` so Cow owns it directly. In this
        // case no mutation occurs and thus also no clone, but the result is
        // still owned because it was never borrowed or mutated.
        let slice = vec![0, 1, 2];
        let mut input = Cow::from(slice);
        match abs_all(&mut input) {
            // TODO
        }
    }

    #[test]
    fn owned_mutation() -> Result<(), &'static str> {
        // Of course this is also the case if a mutation does occur. In this
        // case the call to `to_mut()` returns a reference to the same data as
        // before.
        let slice = vec![-1, 0, 1];
        let mut input = Cow::from(slice);
        match abs_all(&mut input) {
            // TODO
        }
    }
}

题解
// cow1.rs
//
// This exercise explores the Cow, or Clone-On-Write type. Cow is a
// clone-on-write smart pointer. It can enclose and provide immutable access to
// borrowed data, and clone the data lazily when mutation or ownership is
// required. The type is designed to work with general borrowed data via the
// Borrow trait.
//
// This exercise is meant to show you what to expect when passing data to Cow.
// Fix the unit tests by checking for Cow::Owned(_) and Cow::Borrowed(_) at the
// TODO markers.
//
// Execute `rustlings hint cow1` or use the `hint` watch subcommand for a hint.

use std::borrow::Cow;

fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> {
    for i in 0..input.len() {
        let v = input[i];
        if v < 0 {
            // Clones into a vector if not already owned.
            input.to_mut()[i] = -v;
        }
    }
    input
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn reference_mutation() -> Result<(), &'static str> {
        // Clone occurs because `input` needs to be mutated.
        let slice = [-1, 0, 1];
        let mut input = Cow::from(&slice[..]);
        // 这里用了 Cow::Owned 相当于会拷贝一份
        match abs_all(&mut input) {
            Cow::Owned(_) => Ok(()),
            _ => Err("Expected owned value"),
        }
    }

    #[test]
    fn reference_no_mutation() -> Result<(), &'static str> {
        // No clone occurs because `input` doesn't need to be mutated.
        let slice = [0, 1, 2];
        let mut input = Cow::from(&slice[..]);
        match abs_all(&mut input) {
            Cow::Borrowed(_) => Ok(()),
            _ => Err("Expected owned value"),
        }
    }

    #[test]
    fn owned_no_mutation() -> Result<(), &'static str> {
        // We can also pass `slice` without `&` so Cow owns it directly. In this
        // case no mutation occurs and thus also no clone, but the result is
        // still owned because it was never borrowed or mutated.
        let slice = vec![0, 1, 2];
        let mut input = Cow::from(slice);
        match abs_all(&mut input) {
            Cow::Owned(_) => Ok(()),
            _ => Err("Expected owned value"),
        }
    }

    #[test]
    fn owned_mutation() -> Result<(), &'static str> {
        // Of course this is also the case if a mutation does occur. In this
        // case the call to `to_mut()` returns a reference to the same data as
        // before.
        let slice = vec![-1, 0, 1];
        let mut input = Cow::from(slice);
        match abs_all(&mut input) {
            Cow::Owned(_) => Ok(()),
            _ => Err("Expected owned value"),
        }
    }
}

标签:count,Cow,rCore,sun,let,Rc,println,OS,Rust
From: https://blog.csdn.net/Serio_gugugu/article/details/143077316

相关文章

  • PostgreSQL免安装版本
    下载压缩包官网地址:https://www.enterprisedb.com/download-postgresql-binaries解压并新建data文件夹进入pgsql文件夹,新建data文件夹cmd进入pgsql/bin目录,初始化数据库,并设置数据库密码初始化命令:initdb.exe-DF:\pgsql\data-EUTF-8--locale=chs-Uroot-W......
  • DOS命令的使用
    管道命令批处理执行文件将DOS命令写入文件中文件扩张名为。bat文件中rem为注释不能换行DOSfor循环for/L%%in(1,1,10)doeach%%i括号中表示:从1到10每次加1写入数据批处理for/L%%iin(1,1,100)doecho所想写入的内容使用批处理添加IP地址添加IP:......
  • P6533 [COCI2015-2016#1] RELATIVNOST 题解
    考虑当$q=0$时怎么做。注意到性质$c\le20$,因此不妨正难则反,将**至少有$c$个人购买彩色画**的方案数转化为总方案数减去**不足$c$人购买彩色画的方案数**。这个是一个类似凑数的dp,不妨考虑背包。我们有$f_{i,j}$表示前$i$人中**恰好**$j$人购买彩色画的方......
  • 【PS2024】编辑修改、图像制作、广告创意等 PS软件下载安装Adobe Photoshop
    目录一、软件简介1.1AdobePhotoshop概述1.2版本历史1.3系统要求二、下载与安装2.1下载方式2.2安装步骤三、功能介绍3.1基本图像编辑功能3.2高级图像处理功能3.3设计与排版功能四、操作指南4.1界面布局4.2常用快捷键4.3操作技巧一、软件简介1.1......
  • Transformer中的位置编码(Positional Encoding)
    Transformer中的位置编码(PositionalEncoding)标准位置编码原理上Transformer是无法隐式学到序列的位置信息的,为了可以处理序列问题,Transformer提出者的解决方案是使用位置编码(PositionEncode/Embedding,PE)[1][2].大致的处理方法是使用sin和cos函数交替来创建位置编码PE,......
  • Java自定义函数查看OS的File Cache — 从原理到实战
    全文目录:开篇语......
  • HarmonyOS设置组件导航
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(MaoistLearning)➤博客园地址:为敢技术(https://www.cnblogs.com/strengthen/ )➤GitHub地址:https://github.com/strengthen➤原文地址:https://www.cnblogs.com/strengthen/p/......
  • rust操作mysql增删改查
    toml[dependencies]mysql="25.0.0"[[bin]]name="mysql"path="src/mysql.rs"mysql.rsusemysql::*;usemysql::prelude::*;fnmain(){leturl="mysql://root:root@localhost:3306/fiber";letpool=Po......
  • R语言机器学习算法实战系列(五)GBM算法+SHAP值 (Gradient Boosting Machines)
    禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者!文章目录介绍教程下载数据加载R包导入数据数据预处理数据描述数据切割调节参数构建模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROCCurvePRCCurve特......
  • WheelChoose组件的用法
    文章目录1.概念介绍2.使用方法3.代码与效果3.1示例代码3.2运行效果4.内容总结我们在上一章回中介绍了"如何实现Numberpicker"相关的内容,本章回中将介绍wheelChoose组件.闲话休提,让我们一起TalkFlutter吧。1.概念介绍我们在本章回中介绍的whee......