【CS110L】Rust语言 Lecture3-4 笔记

发布于:2024-09-19 ⋅ 阅读:(11) ⋅ 点赞:(0)

第三讲 所有权:移动与借用&

为什么学习Rust?— 为了写出更安全的c/c++代码。Rust是人们对于c/c++缺点的回应,这个回应也有一些不足。

有些库会自己释放,给你指针,你不能用free,而是库提供的

一个包含了指向内存其他地方的指针的结构体的所有权传递问题在Rust和c/c++中都是有挑战的。Rust在编译时强迫你解决清除所有所有权的问题。

例1
// eg1.rs
fn main() {
    let s = String::from("hello");
    s.push_str(" world");
}

错,改为let **mut** s = String::from("hello");

例2
//eg2.rs
fn om_nom_nom(s: String) {
    println!("{}", s);
    // 释放s
}

fn main() {
    let s = String::from("hello");
    om_nom_nom(s);
    // 到这里s的生命周期就结束了
    om_nom_nom(s);
}

错。相比以下c代码,free有四种放置方式,都可以通过编译。但只有一种是正确的。其余都会在运行时出现漏洞。

Rust起初感觉上是反直觉的,但是不容易错的。

//eg2.c
void om_nom_nom(char* s) {
    printf("%s\n", s);
}
int main() {
    char* s = strup("hello");
    om_nom_nom(s);
    om_nom_nom(s);
    free(s);
}

eg2.rs改法:

//eg2.rs
fn om_nom_nom(s: &String) {
    println!("{}", s);
}

fn main() {
    let s = String::from("hello");
    om_nom_nom(&s); // 传引用
    om_nom_nom(&s);
    // s在这里释放
}
例3
// eg3.rs
fn main() {
    let s = String::from("hello");
    let s1 = &s;
    let s2 = &s;
    println!("{} {} {}", s, s1, s2);
}

对,s1 s2都是不可更改的副本

// eg3.rs
fn main() {
    let mut s = String::from("hello");
    let s1 = &mut s;
    let s2 = s;
    println!("{} {} {}", s, s1, s2);
}

错,rust的引用模型就是只有一个可改的,或者多个不可改的

// eg3.rs
fn main() {
    let mut s = String::from("hello");
    let s1 = &mut s;
    println!("{} {}", s, s1);
}

错, Rust 的借用规则不允许在同一作用域内同时存在可变引用和原始变量

// eg3.rs
fn main() {
    let mut s = String::from("hello");
    let s1 = &mut s;
    println!("{} {}", s1);
    println!("{} {}", s);
}

对,编译器看到了在使用s1时没有用过s,那就可以

// eg3.rs
fn main() {
    let mut s = String::from("hello");
    let s1 = &mut s;
    println!("{} {}", s);
    println!("{} {}", s1);
}

错误处理(开头)

Q:这段代码哪里有问题?
在这里插入图片描述
A:(本节课只讲第一点 —— 空指针)
在这里插入图片描述

为什么空指针如此危险,我们能做什么以应对?— 引出Option

None和值不会混在一起。

fn feeling_lucky() -> Option<String> {
    if get_random_num() > 10 {
        Some(String::from("lucky"))
    } else {
        None
    }
}
is_none()函数

if feeling_lucky().is_none()

unwrap_or()函数

let message = feeling_lucky().unwrap_or(String::from("No lucky"));

常见用法
match feeling_lucky() {
	Some(message) => {
		println!("Got message: {}", message);
	},
	None => {
		println!("No message returned.");
	}
}

第四讲 代码实践:链表

Box

在堆上分配,类似于c++的unique_ptr,优点:指针超出作用域后销毁,那块内存自动释放。

fn main() {
    let x: Box<u32> = Box::new(10);
    println!("{}", *x); // *x改为x,输出同样是10,因为自动解引用
}
// 输出:10
节点和链表的定义
struct LinkedList {
    head : Option<Box<Node>>, // ,
    size : usize,
}

struct Node {
    value : u32,
    next : Option<Box<Node>>,
}

Q: next : Box<&Node> 行吗?

A:不行。我们在这里需要的是实际拥有下一个节点,不然考虑你从哪里借用。一个Box就是一个指针,指向heap上某处的拥有指针。只要Box存活(指在生命周期内),Node必须存活。

节点和链表的构造函数
impl Node {
    pub fn new(value: u32, next: Option<Box<Node>>) -> Node {
        Node {value: value, next: next}
    }
}

impl LinkedList {
    pub fn new() -> LinkedList {
        LinkedList {head: None, size: 0}
    }
}
判空 得到size
pub fn get_size(&self) -> usize {
    self.size // (*self).size 也可。从习惯上讲,不需要的话不要显式解引用
}

pub fn is_empty(&self) -> bool {
    self.size == 0
}
插:take
let mut x : Option<u32> = Some(5);
let x_ref : &mut Option<u32> = &mut x;
println!("{:?}", x_ref.take());
println!("{:?}", x);
// Some(5)
// None
Push Pop
pub fn push(&mut self, value: u32) {
    let tmp_node: Box<Node> = Box::new(Node::new(value, self.head.take()));
    self.head = Some(tmp_node); // 此Some对应head的类型是Option
    self.size += 1;
}

pub fn pop(&mut self) -> Option<u32> {
    let tmp_node: Box<Node> = self.head.take()?; //问号
    self.head = tmp_node.next;
    Some(tmp_node.value) // 此Some对应返回值的Option
}

关于?符号的解释:? 是错误传播操作符,它用于在遇到 Err 值时提前退出函数,并返回错误。当 ? 被用于 Option 类型时,如果 OptionNone,那么它会导致函数返回 None,并且提前退出函数。如果 OptionSome(value),那么 ? 会提取出 value 并继续执行。


网站公告

今日签到

点亮在社区的每一天
去签到