Rust | 2 所有权与借用

2022-01-12
1191字
3分钟

所有权

申请内存和释放空间主要有三种流派

  • 垃圾回收机制(GC):在程序运行时不断寻找不再使用的内存, 如 Java、Go
  • 手动管理内存的分配和释放, 在程序中,通过函数调用的方式来申请和释放内存,如 C++
  • 通过所有权来管理内存,编译器在编译时会根据一系列规则进行检查

不安全代码

int* foo(){
    int a;
    a = 100;
    char* c = "xyz";
    return &a;
}

a会造成 悬空指针 的问题,c会占用内存回收较晚

栈(Stack)与堆(Heap)

  • 后进先出,栈中的所有数据都必须占用已知且固定大小的内存空间,假设数据大小是未知的,那么在取出数据时,你将无法取到你想要的数据。

  • 存储大小未知或可能变化的数据,操作系统在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的指针, 该过程被称为在堆上分配内存,有时简称为 “分配”(allocating)。接着,该指针会被推入中,因为指针的大小是已知且固定的,在后续使用过程中,你将通过栈中的指针,来获取数据在堆上的实际内存位置,进而访问该数据。

所有权原则

  1. Rust 中每一个值都 有且只有 一个所有者(变量)
  2. 当所有者(变量)离开作用域范围时,这个值将被丢弃(free)

变量与作用域

从创建到离开作用域,和其他语言一样

变量绑定的数据交互

转移所有权

let x = 5;
let y = x;
let s1 = String::from("hello");
let s2 = s1;

在这里,由于5 是基本数据类型,两个值都是自动拷贝,存在栈中。

String 是一个复杂类型,由存储在栈中的堆指针字符串长度字符串容量共同组成,所以赋值s2 后,s1 就失效了,相当于移动

克隆(深拷贝)

自动的复制都不是“深拷贝”,如果需要,可以使用clone 方法。

拷贝(浅拷贝)

任何基本类型的组合可以是 Copy 的,不需要分配内存或某种形式资源的类型是 Copy

在这里,Copy 作为一个变量可以存在栈中的特征

  • 所有整数类型,比如 u32
  • 布尔类型,bool,它的值是 truefalse
  • 所有浮点数类型,比如 f64
  • 字符类型,char
  • 元组,当且仅当其包含的类型也都是 Copy 的时候。比如,(i32, i32)Copy 的,但 (i32, String) 就不是。

函数传值与返回

函数和变量名一样,可以拥有值的所有权,包括传入的参数和返回值

引用与借用

Rust通过借用(Borrowing) 这个概念,来获取变量的引用

引用与解引用

let x = 5;
let y = &x;//引用
assert_eq!(5,x);
assert_eq!(5,*y);//解引用

不可变引用与可变引用

fn main(){
    let s = String::from("hello");
    change(&s);//默认的引用也是不可变的
}
fn chnage(some_string: &String){
    some_string.push_str(", world");
}

需要和变量一样加入&mut

fn main(){
    let s = String::from("hello");
    change(&mut s);//默认的引用也是不可变的
}
fn chnage(some_string: &mut String){
    some_string.push_str(", world");
}

限制:

  • 同一作用域,特定数据只能有一个可变引用

    可以使用大括号限制可变引用作用范围

  • 可变引用与不可变引用不能同时存在

  • 引用作用域的结束位置从花括号变成最后一次使用的位置

    Non-Lexical Lifetimes(NLL) 优化

悬垂引用(Dangling References)

fn dange()-> &String{
    let s = String::from("hello");
    &s
}//返回的&str是无效的

解决方法是直接返回String,转移所有权

Avatar
Zhang.Mathias 巧者劳而智者忧,无能者无所求,饱食而遨游,泛若不系之舟