Rust 所有权

认识所有权

所有权是 rust独特的功能,它让 rust无需垃圾回收即可保证内存安全。

什么是所有权

Rust核心功能之一是所有权。所有运行的程序都必须管理其使用的计算内存的方式。一些语言具有内存回收机制,在运行时不断地寻址不再使用的内存。在另一些语言中,程序员必须亲自分配和释放内存。Rust则使用第三中方式:通过所有权系统管理内存,在编译时会根据一系列规则检查进行检查。在运行时,所有权的任何功能不会减慢程序。

所有权规则

  • Rust中的每一个值都有一个被称为其 所有者的变量
  • 值有且只有一个所有者
  • 所有者离开作用域,这个值将被丢弃

变量作用域

{
  // s 在这里无效,尚未声明
  let s = "abc";
  // 使用 s
} // 此作用域已结束 s 不在有效

内存与分配

就字符串字面值来说,编译时就知道其内容,所以文本被直接硬编码到最终可执行文件。这使得字,值快速且高效。这是因为字面值的不可变性。不幸的是,我们不能为了每一个在编译时大小未知的文本而将一块内存放入二进制文件中,并且它的大小可能随着程序运行发生变化。

对于 String类型,为了支持一个可变,可增长的文本片段,需要在堆上分配一块在编译时未知大小的内存来存放内容。
这意味着:

  • 必须在运行时向操作系统请求内存。
  • 需要一个当我们处理完String时将内存返回给操作系统的方法。
    第一部分:当调用 String::from 时,它实现了请求所需内存。
    第二部分:内存在拥有它的变量离开作用域时就被自动释放。
{
  let s = String::from("abc");
  // 使用 s
} // 此作用域结束 s 不再有效

当变量离开作用域时,Rust给我们调用了一个特殊的 drop函数。

变量与数据交互的方式(一):移动

let x = 5;
let y = x;

这里做了什么:将 5绑定到 x, x拷贝到y。现在 xy都等于 5。因为是已知的固定大小的值,所以两个 5被放入到了栈中。

现在看看String的版本:

let s1 = String::from("abc");
let s2 = s1;

看起来和上面的代码非常相识,现在假设和他们的运行方式相识:s1 拷贝到 s2。不过事实上完全不是这样。
String::from在堆内存申请了空间,s1指向了申请的内存。let s2 = s1只是 s2拷贝了s1指向的内存地址,长度和容量,并没有复制堆上的数据。如果Rust也复制了堆上的数据,那么会对运行时的性能造成非常大的影响。

之前提到过变量离开作用域后会自动调用drop函数并清理堆内存。这里s1s2都指向同一堆内存地址。当s1s2离开作用域时,他们会释放相同的内存,这可能是一个二次释放的错误,两次释放相同内存会导致内存污染,它肯能会导致潜在的安全漏洞。

为了确保内存安全,这种场景下Rust的处理有另一个细节值得注意。与其拷贝分配的内存,Rust则认为s1不再有效,在s1离开作用域后不需要清理任何东西,在s2创建只后s1也无法使用了。尝试编译会得到一个错误:

error[E0382]: borrow of moved value: `s1`
 --> .\rust所有权.rs:4:20
  |
2 |     let s1 = String::from("abc");
  |         -- move occurs because `s1` has type `std::string::String`, which does not implement the `Copy` trait
3 |     let s2 = s1;
  |              -- value moved here
4 |     println!("{}", s1);
  |                    ^^ value borrowed here after move

error: aborting due to previous error

如果你在其他语言听说过浅拷贝深拷贝,这看起来像浅拷贝。不过因为Rust使第一个变量无效了,这操作被成为移动

变量与数据交互的方式(二):克隆

如果我们确实需要深度复制String中堆上的数据。而不仅仅是栈上的数据,可以用一个叫做clone的通用函数。

let s1 = String::from("hello");
let s2 = s1.clone();

println!("s1 = {}, s2 = {}", s1, s2);

这段代码能正常运行,这里堆上的数据复制了

只在栈上的数据:拷贝

let x = 5;
let y = x;

println!("x = {}, y = {}", x, y);

这段代码似乎和我们刚刚学到的矛:没有使用 clone,不过依然有效且没有被移动y中。

原因是像整形这样的在编译时已知大小的类型被整个储存在栈上,所以拷贝的值是快速的。这也意味着没有理由在创建 y后使 x无效。换句话说这里的深拷贝浅拷贝没有什么不同。

Rust 有一个叫做 Copy trait 的特殊注解,可以用在类似整型这样的存储在栈上的类型。
一些Copy的类型:

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

所有权与函数

将值传递给函数在语义上与给变量赋值相似。向函数传递值可能会移动或复制,就像复制语句一样。

fn main() {
  let s = String::from("abc");
  f1(s); // s 移动到了 f1
  // 这里 s 不再有效
   let x = 5;
   f2(x); // 这里 copy 了x,x 还是可以使用
} // 这里 x 先移出了作用域,然后是 s。因为 s 的值已被移走,这里不会有特殊操作

fn f1(s: String) {
  println!("{}", s);
} // 这里 s 移出了作用域,并调用 drop 方法。占用的内存被释放

fn f2(x: i32) {
  println!("{}", x);
} // 这里 x 移出了作用域,不会有特殊操作

返回值与作用域

fn main() {
    let s1 = f1(); // f1() 将返回值 移给 s1
    let s2 = String::from("hello"); // s2 进入作用域
    let s3 = f2(s2); // s2 被移动到 f2 中,它也将返回值移给 s3
} // 这里 s3 移出作用域,调用 drop,s2 移出作用域,但已被移走不会做任何操作。s1 移出作用域调用 drop

fn f1() -> String {// 将返回值移动给调用方
    String::from("abc")
}

fn f2(s: String) -> String {// s 进入作用域
    s // 返回 s 并移出作用域给调用方
}

在一个函数中都获取作用域,并接着返回所有权有些啰嗦。如果函数使用一个值但不获取所有权该怎么办呢?Rust有一个功能叫做引用

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容