rust所有权、切片、引用


给博客加了个二维码手机阅读,点标题后面就可以。

还有添加了图片全屏展示。

学习视频:B站

所有权

Rust最独特的特性,无需GC就可以保证内存安全。

所有程序运行时都必须管理使用计算机内存的方式:

  • 垃圾收集
  • 程序员显示分配和释放内存
  • Rust:所有权系统管理内存

Rust需要更加关注栈内存和堆内存。stack vs heap。

所有权解决的问题

  • 跟踪代码哪些部分在使用heap的哪些数据
  • 最小化堆上的重复数据量
  • 清理heap未使用的数据

所有权规则

  • 每个值有一个变量,这个变量是值的拥有者
  • 每个值同时只能有一个所有者
  • 当所有者超出作用域时,将被删除

String类型内存和分配

image-20221117144109620

String类型的值可以修改,而字符串字面值不能修改。(处理内存的方式不同)

  • 字符串字面值编译时就知道内容,被硬编码到可执行文件。速度快,效率高
  • String类型,heap上分配内存保存编译时未知的文本内容

Rust当拥有它的变量离开作用域后,会自动将内存归还给操作系统,如String会自动调用drop函数。

变量和数据交互方式

String:

image-20221117162751168

不是浅拷贝,而是移动,s1失效了。

image-20221117162719225

隐含原则:Rust不会自动创建数据的深拷贝。

深拷贝clone()

fn main() {
    let s1 = String::from("hello");
    // 深拷贝,正确
    let s2 = s1.clone();
    println!("{}",s1); // 正确
}

stack上的数据:复制,旧变量仍可使用

fn main() {
    let a = 5;
    let b = a;
    println!("{} {}",a,b); // 5 5
}

Copy trait的类型

  • 简单标量的组合
  • 任何需要分配内存或资源的都不是copy trait

特殊情况:

Tuple,如果所有字段都是copy,那这个tupe就是copy的。

  • (i32, i32)
  • (i32, String)不是

函数参数传递

fn main() {
    let s = String::from("Hello World");
    take_ownership(s);
    // 此时这里的s已经失效了
    
    // println!("{}",s); // value moved
    let x = 5;
    makes_copy(x);
    println!("{}",x);
}

fn take_ownership(some_string: String) {
    println!("{}",some_string);
}

fn makes_copy(some_number: i32) {
    println!("{}",some_number);
}

返回值和作用域

fn main() {
    let s1 = gives_ownership();

    let s2 = String::from("hjell");

    let s3 = takes_and_gives_back(s2);
}

fn gives_ownership() -> String {
    let s = String::from("hi");
    s
}

fn takes_and_gives_back(s: String) -> String {
    s
}

引用和借用

fn main() {
    let mut s1 = String::from("hi");
    let len = get_len(&mut s1);
    // 在一块数据,只能有一个可变引用,编译时防止数据竞争
    println!("{}: {}",s1,len)
}

fn get_len(s: &mut String) -> usize {
    s.push_str("hi");
    s.len()
}

可以通过创建新的作用域,来允许非同时创建多个可变引用。

{
    let s2 = &mut s1;
}
let s3 = &mut s1;

image-20221117170804160

切片

slice,不持有所有权。

demo:查找第一个空格

不切片:

fn main() {
    // 查找第一个空格
    let mut s = String::from("hi23 rae");

    let wordIndex = first_world(&s);
    println!("{}",wordIndex);
}

fn first_world(s: &String) ->usize {
    let bytes = s.as_bytes();
    for (i,&item) in bytes.iter().enumerate() {
        if item == b' ' {
            return i;
        }
    }
    s.len()
}

切片:字符串切片是指向字符串中其中一部分内容的引用。

fn main() {
    // 查找第一个空格
    let mut s = String::from("hello world");
    // let hello = &s[0..5];
    // let world =&s[6..11];

    let word = first_world(&s);
    println!("{}",word);
}

fn first_world(s: &String) ->&str {
    let bytes = s.as_bytes();
    for (i,&item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[..i];
        }
    }
    &s[..]
}