rust04所有权

所有权指涵盖的范围有: 变量作用域、垃圾处理机制、引用,围绕着几种行为来阐述所有权概念。

 

变量作用域(Scope)

任何编程语言中都存在变量作用域的概念, Rust中的变量作用域与其他编程语言也基本一致;作用域大致上会分为: 全局作用域, 函数内作用域.

全局作用域

在下面这段代码中, PROGRAM常量和main函数就是一个全局作用域; 反过来讲就是当前程序代码中,全局作用域拥有两个对象, 第一个对象是 PROGRAM 常量, 第二个对象是 main 函数。

const PROGRAM: &'static str = "Rust";

fn main() {
    println!("{}", PROGRAM);
}


# 作用域的形式可以这么理解, global_scope中有两个对象.
global_scope = {
    "const PROGRAM": "Rust"
    "fn main": "println!({}, PROGRAM)"
}
函数内作用域

变量不能let(定义)在全局作用域中, 所以全局作用域能做的事情并不多: 定义常量、定义函数、引用其他模块文件; 更多的合法操作实在函数作用域中完成: 定义变量、打印变量、逻辑计算、逻辑判断、函数调用和执行等.

const PROGRAM: &'static str = "Rust";

fn main() {
    let s = "abc";                      // 定义变量
    let sf = simple_function();         // 执行函数
    let sum = 10 + 15;                  // 逻辑计算
    if sum > 0 {                        // 逻辑判断
        println!("{} {} {}", PROGRAM, s, sum);  // 打印常量和变量
    }
}

fn simple_function() {
    let sf = "Simple Function";         // 定义变量
    sf                                  // return 变量
}


# 作用域的形式可以这么理解, global_scope中有三个对象, 其中
# main对象中又有一个二级作用域, simple_function对象中也有一个
# 二级作用域.
global_scope = {                        // 作用域

    "const PROGRAM": "Rust",
    
    "main": {                           // 作用域
        "let s": "abc",
        "let sf": "Simple Function",
        "let sum": 25,
    },
    
    "simple_function": {                // 作用域
        "let sf": "Simple Function"
    }
}

 
 

垃圾处理机制

像其他具备GC回收机制编程语言一样,当程序执行完成并跳出某个作用域(通常指的是一个函数)时,该作用域中的所有变量将会失效(被回收);除此之外, Rust对垃圾回收这件事情上还具备其他的能力和行为, 例如: 当需要对存储在堆(Heap)中的数据进行复制时, 被赋值对象将具备赋值对象的数据,而赋值对象将会被回收。

离开作用域时回收数据
fn simple_function() {
    let sf = "Simple Function";    // sf变量开始生效
    println!("{}", sf);            // sf变量仍然生效
}                                  // sf变量不再生效

fn main() {
    simple_function();             // 当该函数执行完成后, sf变量就会被回收
    println!("{}", sf);            // 报错: sf变量不存在.
}
变量传递后即刻失效

fn simple_function(sf: String) {
    println!("simple_function: {}", sf);
}

fn main() {
    let s = String::from("hello");
    simple_function(s);
    println!("main: {}", s);
}



# 报错
Compiling ownership v0.1.0 (file:///opt/learn_rust/ownership)
error[E0382]: use of moved value: `s`
  --> src/main.rs:10:26
   |
9  |     simple_function(s);
   |                     - value moved here
10 |     println!("main: {}", s);
   |                          ^ value used here after move
   |
   = note: move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait

通过return让它重回原作用域

fn simple_function(sf: String) -> String {  // -> String 声明返回值类型
    println!("simple_function: {}", sf);
    sf
}

fn main() {
    let s = String::from("hello");
    let s = simple_function(s);     // 利用返回值, 重新定义变量.
    println!("main: {:?}", s)       
}

数据存储在栈上时, 变量再赋值不会回收原变量
fn main() {
    let a = 10;                 // rust会将固定不变的值存储在栈(Stack)上.
    let b = a;                  // rust对栈上的数据默认采取深复制.
    println!("{} {}", a, b);
}

fn main() {
    let a = "abc";              // 与上面是一样的.
    let b = a;                  
    println!("{} {}", a, b);
}
当变量存储再堆上时, 变量再赋值就会回收原变量(所有权转移)
fn main() {

    // rust会将这种未知大小的数据存储在堆(Heap)上.
    // 因为String::from这种结构的数据支持push_str
    // 来扩充它的大小, 因此从本质上来讲是未知大小.
    let a = String::from("hello");  
    
    // rust对堆上的数据默认采取移除上一个变量创建
    // 新变量的机制, 这种做法在术语上叫做所有权转移.
    let b = a;                  
    
    // 这里会报错, a变量已被移除
    println!("{} {}", a, b)     
}


# 报错信息
Compiling ownership v0.1.0 (file:///opt/learn_rust/ownership)
error[E0382]: use of moved value: `a`
  --> src/main.rs:10:23
   |
7  |     let b = a;
   |         - value moved here
...
10 |     println!("{} {}", a, b)
   |                       ^ value used here after move
   |
   = note: move occurs because `a` has type `std::string::String`, which does not implement the `Copy` trait

 
 

引用(reference)

不可变更引用(默认)
fn main() {
    let a = 10;
    
    // b变量是一个指针, 它并没有实际数据的所有权.
    let b = &a;
    
    // 引用是在栈上创建一个指针指向栈数据.
    // 它比在栈上深复制更轻量.
    println!("{} {}", a, b)     
}
可变引用
fn main() {
    let mut a = "hello";
    a + "b"
    println!("{}", a)
}
引用的注意事项
可变对象不能被多次引用, 这会导致数据竞争.
fn main() {
    let mut a = 10;
    let b = &mut a;
    let c = &mut a;
}


# 报错
Compiling ownership v0.1.0 (file:///opt/learn_rust/ownership)
error[E0499]: cannot borrow `a` as mutable more than once at a time
  --> src/main.rs:18:18
   |
17 |     let b = &mut a;
   |                  - first mutable borrow occurs here
18 |     let c = &mut a;
   |                  ^ second mutable borrow occurs here
19 | }

可变对象被可变引用之后, 再次引用会导致数据不一致.
fn main() {
    let mut a = 10;

    // 可变对象被可变引用走了
    let b = &mut a;

    // 这里会报错, 因为数据状态任何时刻都可能会改变,
    // 这是不可预期的, 所以rust不允许这种情况的出现.
    println!("{} {}", a, b)
}

可变对象被可变引用之后,数据不一致的解决办法
fn simple_function(sf: &mut String) -> String {
    sf.push_str(" world!");
    let new_sf = sf.clone();
    new_sf
}

fn main() {
    let mut a = String::from("hello");

    // 站在变量的角度来讲: &mut a 的专业术语为<可变引用>
    // &mut a 当做参数传递给函数时, 专业术语为<可变借用>
    let b = simple_function(&mut a);  

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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,504评论 18 399
  • 1.项目经验 2.基础问题 3.指南认识 4.解决思路 ios开发三大块: 1.Oc基础 2.CocoaTouch...
    阳光的大男孩儿阅读 4,956评论 0 13
  • 重点掌握 3 类对象和方法 对象就是一个物体 类的独特存在就是一个实例,对实例进行操作叫做方法。方法可以应用于类或...
    Coder大雄阅读 1,253评论 0 2
  • 当化妆品专柜的小姑娘嗲声嗲气的唤我声"姐 ",当美容院的心机大妈喋喋不休的吐槽我的法令纹。我觉得我的闪闪玻璃心瞬间...
    紫果冻阅读 365评论 0 1
  • 总有一首歌能唱出你的心声,总有一句话会触动内心的柔软。谁的青春不留点遗憾呢? “真的想寂寞的时候有个伴,日子再忙也...
    总能长大的蒲公英阅读 268评论 0 3