Rust基础学习-06-所有权、作用域

本篇博客我们来解释几个名词,作用域所有权所有权移动

栈是在代码运行时,可供使用的一块内存。它的存取数据方式是先进后出,或者说后进先出。想象有一个箱子,你往里放本子,最先放入的本子,是在箱子底下,当你要使用本子时,总是从顶上取一个使用,也就是取最后放入的一个本子。
因为这种存取数据时总是在栈顶操作,而不需要去内存中寻找一个位置,所以栈的操作是时分迅速的。
还有一个点是,存在栈里的数据,都是以知的固定大小。这一点的意思是,例如要让用户输入一个名字,因为不知道用户会输入多少字符,所以这个数据就无法放在栈中,因为无法事先知道明确的大小。

在编译时大小未知或大小可能变化的数据,要改为存储在堆上。堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。操作系统在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的 指针(pointer)。这个过程称作 在堆上分配内存(allocating on the heap),有时简称为 分配(allocating)

作用域

作用域可以理解为一个东西在程序中的有效范围。对于Rust来说,当一个变量出了作用域后,对应的内存就会自动被释放掉,变量变为无效状态。

{                      // s 在这里无效, 它尚未声明
    let s = "hello";   // 从此处起,s 是有效的
    // 使用 s
}                      // 此作用域已结束,s 不再有效

字符串类型 String

之前在数据类型一节,没有讲到 String,是因为牵扯到堆栈的问题,所以放在这里讲。

fn main() {
    // 像这种直接硬编码在代码里的字符串,是放在栈上的,并且不可改变
    let name = "Jack";
    
    // 使用String::from创建的,是在堆上分配内存,并且是可以改变的
    let mut my_name = String::from("Jack");
    my_name.push_str(", My name is Jack");
    
    // 输出 Jack, My name is Jack
    println!("{}", my_name);
}

当调用 String::from 时,它的实现 (implementation) 请求其所需的内存。这在编程语言中是非常通用的。

所有权

  1. Rust 中每一个值都有一个被称为所有者的变量
  2. 值,有且只有一个所有者
  3. 当所有者(变量)离开作用域时,这个值被丢弃,内存被释放

移动

先看下面一段代码

fn main() {
    let x = 10;
    let y = x;
    println!("x: {}, y:{}", x, y);
    
    let name1 = "Fred";
    let name2 = name1;
    println!("name1: {}, name2: {}", name1, name2);
}

很正常,最后输出了 x: 10, y:10name1: Fred, name2: Fred

再看下面这段代码

fn main() {
    let name1 = String::from("Fred");
    println!("name1: {}", name1);

    let name2 = name1;
    println!("name2: {}", name2);

    // 编译出错,这句会出错
    println!("name1 again: {}", name1);
}

为什么加了最后一句会编译出错呢,这里涉及到一个概念,移动。首先 name1 指向的值是分配在堆上的。当将 name1 赋值 给 name2后,在有一些编程语言,两个变量会指向同一块堆内存区域,但是对于Rust来说,不是这样的,Rust在这里会直接让 name1 失效,避免两个指针指向同一块堆内存。因为 Rust 会自动释放内存,这样可以避免当两个变量超出作用域时,导致重复的内存释放问题。将 name1 赋值给 name2,这个操作叫做移动,name1移动到了name2,移动后,name1自动失效,所以最后一句访问 name1 会编译出错。

更详细的内容 官方文档

这里要记住,对于那些固定大小的数据类型,i32, f32, boolchar 等不会存在移动的问题。但是对于存储在上的数据,不管是String还是后面自定义的数据类型,这样的操作都会触发移动

有没有办法将指上堆内存的变量赋值给另一个变量不触发移动呢?有!方法就是克隆,看下面的代码。

fn main() {
    let name1 = String::from("Fred");
    println!("name1: {}", name1);

    let name2 = name1.clone();
    println!("name2: {}", name2);

    println!("name1 again: {}", name1);
}

和之前的代码只有第5行变了,当调用了 clone()函数后,会导致 name1 指向的堆上的内存复制一份。所以这里就没有移动。String内部实现了 clone(),当我们自定义数据结构时,如果要有克隆功能,需要自己实现 clone()方法。这个后面会讲到。

移动与函数

说完了移动,就需要说一下移动和函数相关的东西。如果将一个值作为参数,去调用一个函数,如果这个值是在栈上,那么不会发生什么,但是如果这个值是分配在堆上,那么它会移动到函数内部。

看下面的代码(注意看代码的注释)

fn main() {
    let name1 = String::from("Fred");
    println!("name1: {}", name1);

    // name1 的值移动了函数里
    takes_ownership((name1));

    // name1 已经无效,这里再使用就会编译出错
    // println!("name1 again: {}", name1);

}

fn takes_ownership(str: String) {
    println!("i have ownership: {}", str);
}

下面的代码,函数在结束时将 所有权 返回

fn main() {
    let name1 = String::from("Fred");
    println!("name1: {}", name1);

    // 因为name1不是mut的,所以这里的name1相当于创建了一个
    // 新的变量name1, 本质上并不是之前的
    let name1 = takes_and_gives_back(name1);
    println!("name1 again: {}", name1);
}

fn takes_and_gives_back(str: String) -> String {
    println!("i have ownership: {}", str);
    
    // 这里将值返回,所有权移出函数
    str
}

使用函数时每次都要转移所有权很繁琐,所下一节将介绍引用,不用每次将所有权转来转去

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

推荐阅读更多精彩内容