Rust所有权,可转可借

ownership

《释放堆内存,Rust是怎么做的?所有权!》一文中,我们看到了Rust的不凡身手:只要跳出具有所有权的变量作用域,那么该变量所拥有的堆上内存,就会进行确定性的释放。

{
 let  v: Vec<u8>  =  vec![0;100];
} // v作为数组的所有者,在离开作用域时,销毁了所持有的内存。

这时候,问题来了,如若不能跨越作用域,那么充其量也就是另一种局部变量而已。堆变量的生命周期如何才能跨越作用域呢?

答案是:通过所有权的转移和借用

所有权的转移

赋值即转移(move)

实现Rust所有权的转移,非常简单,赋值即转移

{
  let v: Vec<u8> = vec![0;100];
  let u = v; // 数组所有权由v转移给u
  u //函数结尾没有分号,即代表return
} // u作为数组的所有者(如果未发生转移),在离开作用域时,销毁了所持有的数组内存。

let w = get_vector() // 函数返回变量,再次把数组的所有权转移给w</pre>

上面的示例代码,发生了两次堆上数组所有权的转移:

  1. u8类型的数组在函数内部从堆上申请;

  2. 一开始数组的所有权属于变量v;

  3. 当v赋值给u时,数组的所有权转移到了u;

  4. 当函数返回时,通过赋值给w,数组的所有权发生了第二次转移;

最终通过函数返回值赋值操作,将堆所有权转移到了原作用域之外的变量。

避免野指针

此时,我们问个问题,在函数内部,当v赋值给u,转移数组所有权后,v此时的状态是什么?

回答之前,先复习下Rust所有权的基本特性:

  • Rust中的每个值都有一个对应的变量作为它的所有者;

  • 在同一时间内,只有且仅有一个所有者;

  • 当所有者离开自己的作用域时,它持有的值就会被释放掉。

{
  let v: Vec<u8> = vec![0;100];
  let u = v; // 数组所有权由v转移给u
  println!("{}", v[0]);
}

上述代码,数组所有权由v转移给u后,再去使用v,编译都无法通过,编译器会提示:

error[E0382]: borrow of moved value: v

可见此时的v,已经被废弃了,所以当v离开作用域时,也不会清理任何堆数据。

Rust所有权的唯一性,在编译期就避免了C++的野指针和二次释放。

赋值转移的本质

Rust赋值的本质,包含两件事:

  • 浅拷贝,变量数据指向堆的数据,并未发生变化;

  • 废弃源变量,这是Rust独有的;

所有权借用

借用的使用场景

通过所有权转移,函数传参也可以把所有权传递至函数内部。但是通过转移,源变量就被废弃了,如果在函数调用后还想继续使用源变量,则可以使用借用的方式:

{
  let s1 = String::from("hello");
  let len = calculate_length(&s1); // 借用,而不是转移
  println!("The length of '{}' is {}", s1, len);
}

fn calculate_length(s: &String)->usize{
  s.len()
}// s1离开作用域,堆数据释放

所有权的借用,是通过引用实现的。

顾名思义,通过借用得到的对堆数据的引用,是没有所有权的。借用者离开自己的作用域,当然也不会发生对堆数据的释放。

借用与归还

借用分为两种:

  • 不可变借用,借来,但不能改,通过引用实现;

  • 可变借用,借来,可以改,通过可变引用来实现;

{
 let mut x = String::from("Hello");
 x.push_str(", world");
 let r1 = &x; // 不可变借用
 let r2 = &mut x; // 可变借用
 let r3 = &mut x; // 可变借用
 r3.push_str("!"); // 修改
 println!("r3: {}", r3);
}

在2020年6月的第一版第一次印刷的中文版《Rust权威指南》,第4章,认识所有权,97页提到:

可变引用在使用上有一个很大的限制:对于特定作用域中的特定数据来说,一次只能声明一个可变引用。

但是在我的环境里,rustc 1.44.0 (49cae5576 2020-06-01),这个限制明显放开了一些,上面的代码在我的环境里是可以成功编译和运行的。

“非词法作用域生命周期”的延伸阅读:[译] Rust - None Lexical Lifetimes (NLL) 使用指南

除了借用这个概念,我还归纳了一个概念来解释——归还

  • 借用后,在当前作用域中,最后一次使用,即等于归还;

  • 即便在同一个作用域内,借用后只要归还,就能再次借用;

  • 借用期间,源变量不能修改;

  • 可变借用期间,源变量不可用;

这个借用和归还,就像真实世界里图书馆里的一本书。

Rust之所以要做这些约束,主要是为了解决数据竞争(data race),避免了:两个或两个以上的指针同时访问同一空间时,其中至少有一个指针会向空间中写入数据,且没有同步数据访问的机制。

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