Rust基礎學習-06-所有權(quán)靶壮、作用域

本篇博客我們來解釋幾個名詞怔毛,腾降、作用域拣度、所有權(quán)所有權(quán)移動

棧是在代碼運行時螃壤,可供使用的一塊內(nèi)存抗果。它的存取數(shù)據(jù)方式是先進后出,或者說后進先出奸晴。想象有一個箱子冤馏,你往里放本子,最先放入的本子寄啼,是在箱子底下逮光,當你要使用本子時,總是從頂上取一個使用墩划,也就是取最后放入的一個本子涕刚。
因為這種存取數(shù)據(jù)時總是在棧頂操作,而不需要去內(nèi)存中尋找一個位置乙帮,所以棧的操作是時分迅速的杜漠。
還有一個點是,存在棧里的數(shù)據(jù),都是以知的固定大小驾茴。這一點的意思是盼樟,例如要讓用戶輸入一個名字,因為不知道用戶會輸入多少字符锈至,所以這個數(shù)據(jù)就無法放在棧中晨缴,因為無法事先知道明確的大小。

在編譯時大小未知或大小可能變化的數(shù)據(jù)裹赴,要改為存儲在堆上。堆是缺乏組織的:當向堆放入數(shù)據(jù)時诀浪,你要請求一定大小的空間棋返。操作系統(tǒng)在堆的某處找到一塊足夠大的空位,把它標記為已使用雷猪,并返回一個表示該位置地址的 指針(pointer)睛竣。這個過程稱作 在堆上分配內(nèi)存(allocating on the heap),有時簡稱為 分配(allocating)求摇。

作用域

作用域可以理解為一個東西在程序中的有效范圍射沟。對于Rust來說,當一個變量出了作用域后与境,對應的內(nèi)存就會自動被釋放掉验夯,變量變?yōu)闊o效狀態(tài)。

{                      // s 在這里無效, 它尚未聲明
    let s = "hello";   // 從此處起摔刁,s 是有效的
    // 使用 s
}                      // 此作用域已結(jié)束挥转,s 不再有效

字符串類型 String

之前在數(shù)據(jù)類型一節(jié),沒有講到 String共屈,是因為牽扯到堆棧的問題绑谣,所以放在這里講。

fn main() {
    // 像這種直接硬編碼在代碼里的字符串拗引,是放在棧上的借宵,并且不可改變
    let name = "Jack";
    
    // 使用String::from創(chuàng)建的,是在堆上分配內(nèi)存矾削,并且是可以改變的
    let mut my_name = String::from("Jack");
    my_name.push_str(", My name is Jack");
    
    // 輸出 Jack, My name is Jack
    println!("{}", my_name);
}

當調(diào)用 String::from 時壤玫,它的實現(xiàn) (implementation) 請求其所需的內(nèi)存。這在編程語言中是非常通用的哼凯。

所有權(quán)

  1. Rust 中每一個值都有一個被稱為所有者的變量
  2. 值垦细,有且只有一個所有者
  3. 當所有者(變量)離開作用域時,這個值被丟棄挡逼,內(nèi)存被釋放

移動

先看下面一段代碼

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后惹骂,在有一些編程語言苏携,兩個變量會指向同一塊堆內(nèi)存區(qū)域,但是對于Rust來說对粪,不是這樣的右冻,Rust在這里會直接讓 name1 失效,避免兩個指針指向同一塊堆內(nèi)存著拭。因為 Rust 會自動釋放內(nèi)存纱扭,這樣可以避免當兩個變量超出作用域時,導致重復的內(nèi)存釋放問題儡遮。將 name1 賦值給 name2乳蛾,這個操作叫做移動,name1移動到了name2鄙币,移動后肃叶,name1自動失效,所以最后一句訪問 name1 會編譯出錯十嘿。

更詳細的內(nèi)容 官方文檔

這里要記住因惭,對于那些固定大小的數(shù)據(jù)類型,i32, f32, bool绩衷,char 等不會存在移動的問題筛欢。但是對于存儲在上的數(shù)據(jù),不管是String還是后面自定義的數(shù)據(jù)類型唇聘,這樣的操作都會觸發(fā)移動

有沒有辦法將指上堆內(nèi)存的變量賦值給另一個變量不觸發(fā)移動呢版姑?有!方法就是克隆迟郎,看下面的代碼剥险。

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

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

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

和之前的代碼只有第5行變了,當調(diào)用了 clone()函數(shù)后宪肖,會導致 name1 指向的堆上的內(nèi)存復制一份表制。所以這里就沒有移動。String內(nèi)部實現(xiàn)了 clone()控乾,當我們自定義數(shù)據(jù)結(jié)構(gòu)時么介,如果要有克隆功能,需要自己實現(xiàn) clone()方法蜕衡。這個后面會講到壤短。

移動與函數(shù)

說完了移動,就需要說一下移動和函數(shù)相關的東西。如果將一個值作為參數(shù)久脯,去調(diào)用一個函數(shù)纳胧,如果這個值是在棧上,那么不會發(fā)生什么帘撰,但是如果這個值是分配在堆上跑慕,那么它會移動到函數(shù)內(nèi)部。

看下面的代碼(注意看代碼的注釋)

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

    // name1 的值移動了函數(shù)里
    takes_ownership((name1));

    // name1 已經(jīng)無效摧找,這里再使用就會編譯出錯
    // println!("name1 again: {}", name1);

}

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

下面的代碼核行,函數(shù)在結(jié)束時將 所有權(quán) 返回

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

    // 因為name1不是mut的,所以這里的name1相當于創(chuàng)建了一個
    // 新的變量name1, 本質(zhì)上并不是之前的
    let name1 = takes_and_gives_back(name1);
    println!("name1 again: {}", name1);
}

fn takes_and_gives_back(str: String) -> String {
    println!("i have ownership: {}", str);
    
    // 這里將值返回蹬耘,所有權(quán)移出函數(shù)
    str
}

使用函數(shù)時每次都要轉(zhuǎn)移所有權(quán)很繁瑣芝雪,所下一節(jié)將介紹引用,不用每次將所有權(quán)轉(zhuǎn)來轉(zhuǎn)去

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末婆赠,一起剝皮案震驚了整個濱河市绵脯,隨后出現(xiàn)的幾起案子佳励,更是在濱河造成了極大的恐慌休里,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赃承,死亡現(xiàn)場離奇詭異妙黍,居然都是意外死亡,警方通過查閱死者的電腦和手機瞧剖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門拭嫁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抓于,你說我怎么就攤上這事做粤。” “怎么了捉撮?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵怕品,是天一觀的道長。 經(jīng)常有香客問我巾遭,道長肉康,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任灼舍,我火速辦了婚禮吼和,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘骑素。我一直安慰自己炫乓,他們只是感情好,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著厢岂,像睡著了一般光督。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上塔粒,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天结借,我揣著相機與錄音,去河邊找鬼卒茬。 笑死船老,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的圃酵。 我是一名探鬼主播柳畔,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼郭赐!你這毒婦竟也來了薪韩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤捌锭,失蹤者是張志新(化名)和其女友劉穎俘陷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體观谦,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡拉盾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了豁状。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捉偏。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖泻红,靈堂內(nèi)的尸體忽然破棺而出夭禽,到底是詐尸還是另有隱情,我是刑警寧澤谊路,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布讹躯,位于F島的核電站,受9級特大地震影響凶异,放射性物質(zhì)發(fā)生泄漏蜀撑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一剩彬、第九天 我趴在偏房一處隱蔽的房頂上張望酷麦。 院中可真熱鬧,春花似錦喉恋、人聲如沸沃饶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糊肤。三九已至琴昆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間馆揉,已是汗流浹背业舍。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留升酣,地道東北人舷暮。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像噩茄,于是被迫代替她去往敵國和親下面。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容