成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

為什么需要內(nèi)部可變性

開發(fā) 前端
內(nèi)部可變性提供了極大的靈活性,但是考濾到運行時開銷,還是不能濫用,性能問題不大,重點是缺失了編譯期的靜態(tài)檢查,會掩蓋很多錯誤。

[[419570]]

本文參考 rust book ch15 并添加了自己的理解,感興趣的可以先看看官方文檔

Rust 有兩種方式做到可變性

  • 繼承可變性:比如一個 struct 聲明時指定 let mut, 那么后續(xù)可以修改這個結(jié)構(gòu)體的任一字段
  • 內(nèi)部可變性:使用 Cell RefCell 包裝變量或字段,這樣即使外部的變量是只讀的,也可以修改

看似繼承可變性就夠了,那么為什么還需要所謂的 interior mutability 內(nèi)部可變性呢?讓我們分析兩個例子:

  1. struct Cache { 
  2.     x: i32, 
  3.     y: i32, 
  4.     sumOption<i32>, 
  5.  
  6. impl Cache { 
  7.     fn sum(&mut self) -> i32 { 
  8.         match self.sum { 
  9.             None => {self.sum=Some(self.x + self.y); self.sum.unwrap()}, 
  10.             Some(sum) => sum
  11.         } 
  12.     } 
  13.  
  14. fn main() { 
  15.     let i = Cache{x:10, y:11, sum: None}; 
  16.     println!("sum is {}", i.sum()); 

結(jié)構(gòu)體 Cache 有三個字段,x, y, sum, 其中 sum 模擬 lazy init 懶加載的模式,上面代碼是不能運行的,道理很簡單,當(dāng) let 初始化變量 i 時,就是不可變的。

  1. 17 |     let i = Cache{x:10, y:11, sum: None}; 
  2.    |         - help: consider changing this to be mutable: `mut i` 
  3. 18 |     println!("sum is {}", i.sum()); 
  4.    |                           ^ cannot borrow as mutable 

有兩種方式修復(fù)這個問題,let 聲明時指定 let mut i, 但具體大的項目時,外層的變量很可能是 immutable 不可變的。這時內(nèi)部可變性就派上用場了。

修復(fù)

  1. use std::cell::Cell; 
  2.  
  3. struct Cache { 
  4.     x: i32, 
  5.     y: i32, 
  6.     sum: Cell<Option<i32>>, 
  7.  
  8. impl Cache { 
  9.     fn sum(&self) -> i32 { 
  10.         match self.sum.get() { 
  11.             None => {self.sum.set(Some(self.x + self.y)); self.sum.get().unwrap()}, 
  12.             Some(sum) => sum
  13.         } 
  14.     } 
  15.  
  16. fn main() { 
  17.     let i = Cache{x:10, y:11, sum: Cell::new(None)}; 
  18.     println!("sum is {}", i.sum()); 

這是修復(fù)之后的代碼,sum 類型是 Cell。

其實每一個都是有意義的,比如 Rc 代表共享所有權(quán),但是因為 Rc 里的 T 要求是只讀的,不能修改,所以就要用 Cell 封一層,這樣就共享所有權(quán),但還是可變的,Option 就是常見的要么有值 Some(T) 要么空值 None, 還是很好理解的。

如果不是寫 rust 代碼,只想閱讀源碼了解流程,沒必要深究這些 wrapper, 重點關(guān)注包裹的真實類型就可以。

官網(wǎng)舉的例子是 Mock Objects, 代碼比較長,但是原理一樣。

  1. struct MockMessenger { 
  2.       sent_messages: RefCell<Vec<String>>, 
  3.   } 

最后都是把結(jié)構(gòu)體字段,使用 RefCell 包裝一下。

Cell

  1. use std::cell::Cell; 
  2.  
  3. fn main(){ 
  4.     let a = Cell::new(1); 
  5.     let b = &a; 
  6.     a.set(1234); 
  7.     println!("b is {}", b.get()); 

這段代碼非常有代表性,如果變量 a 沒有用 Cell 包裹,那么在 b 只讀借用存在的時間,是不允許修改 a 的,由 rust 編譯器在 compile 編譯期保證:給定一個對像,在作用域內(nèi)(NLL)只允許存在 N 個不可變借用或者一個可變借用。

Cell 通過 get/set 來獲取和修改值,這個函數(shù)要求 value 必須實現(xiàn) Copy trait, 如果我們換成其它結(jié)構(gòu)體,編譯報錯。

  1. error[E0599]: the method `get` exists for reference `&Cell<Test>`, but its trait bounds were not satisfied 
  2.   --> src/main.rs:11:27 
  3.    | 
  4. 3  | struct Test { 
  5.    | ----------- doesn't satisfy `Test: Copy` 
  6. ... 
  7. 11 |     println!("b is {}", b.get().a); 
  8.    |                           ^^^ 
  9.    | 
  10.    = note: the following trait bounds were not satisfied: 
  11.            `Test: Copy` 

從上面可以看到 struct Test 默認沒有實現(xiàn) Copy, 所以不允許使用 get. 那有沒有辦法獲取底層 struct 呢?可以使用 get_mut 返回底層數(shù)據(jù)的引用,但這就要求整個變量是 let mut 的,所以與使用 Cell 的初衷不符,所以針對 Move 語義的場景,rust 提供了 RefCell。

RefCell

與 Cell 不一樣,我們使用 RefCell 一般通過 borrow 獲取不可變借用,或是 borrow_mut 獲取底層數(shù)據(jù)的可變借用。

  1. use std::cell::{RefCell}; 
  2.  
  3. fn main() { 
  4.     let cell = RefCell::new(1); 
  5.  
  6.     let mut cell_ref_1 = cell.borrow_mut(); // Mutably borrow the underlying data 
  7.     *cell_ref_1 += 1; 
  8.     println!("RefCell value: {:?}", cell_ref_1); 
  9.  
  10.     let mut cell_ref_2 = cell.borrow_mut(); // Mutably borrow the data again (cell_ref_1 is still in scope though...) 
  11.     *cell_ref_2 += 1; 
  12.     println!("RefCell value: {:?}", cell_ref_2); 

代碼來自 badboi.dev, 編譯成功,但是運行失敗。

  1. # cargo build 
  2.     Finished dev [unoptimized + debuginfo] target(s) in 0.03s 
  3. # cargo run 
  4.     Finished dev [unoptimized + debuginfo] target(s) in 0.03s 
  5.      Running `target/debug/hello_cargo` 
  6. RefCell value: 2 
  7. thread 'main' panicked at 'already borrowed: BorrowMutError', src/main.rs:10:31 
  8. note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 

cell_ref_1 調(diào)用 borrow_mut 獲取可變借用,還處于作用域時,cell_ref_2 也想獲取可變借用,此時運行時檢查報錯,直接 panic。

也就是說 RefCell 將借用 borrow rule 由編譯期 compile 移到了 runtime 運行時, 有一定的運行時開銷。

  1. #[derive(Debug)] 
  2. enum List { 
  3.     Cons(Rc<RefCell<i32>>, Rc<List>), 
  4.     Nil, 
  5.  
  6. use crate::List::{Cons, Nil}; 
  7. use std::cell::RefCell; 
  8. use std::rc::Rc; 
  9.  
  10. fn main() { 
  11.     let value = Rc::new(RefCell::new(5)); 
  12.  
  13.     let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil))); 
  14.  
  15.     let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a)); 
  16.     let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a)); 
  17.  
  18.     *value.borrow_mut() += 10; 
  19.  
  20.     println!("a after = {:?}", a); 
  21.     println!("b after = {:?}", b); 
  22.     println!("c after = {:?}", c); 

這是官方例子,通過 Rc, RefCell 結(jié)合使用,做到共享所有權(quán),同時又能修改 List 節(jié)點值。

小結(jié) 

內(nèi)部可變性提供了極大的靈活性,但是考濾到運行時開銷,還是不能濫用,性能問題不大,重點是缺失了編譯期的靜態(tài)檢查,會掩蓋很多錯誤。

 

責(zé)任編輯:武曉燕 來源: 董澤潤的技術(shù)筆記
相關(guān)推薦

2022-07-14 23:27:57

數(shù)據(jù)分析數(shù)據(jù)驅(qū)動可變數(shù)據(jù)

2024-07-05 10:47:15

2023-10-30 23:38:03

Rust編程基礎(chǔ)

2021-03-22 17:16:04

AI 數(shù)據(jù)人工智能

2015-03-19 15:04:06

2022-02-22 15:27:46

數(shù)據(jù)結(jié)構(gòu)容器算法

2011-02-16 09:42:04

DevOps

2023-05-24 21:08:00

Linux發(fā)行版

2023-10-20 08:18:17

Python數(shù)據(jù)類型

2023-06-27 08:19:11

2017-09-26 09:50:18

2015-04-16 15:42:21

關(guān)系型數(shù)據(jù)庫NoSQL

2022-06-28 14:54:26

加密貨幣數(shù)組貨幣安全

2015-10-12 08:56:27

Java不可變

2020-04-01 11:19:03

物聯(lián)網(wǎng)LPWANIOT

2022-12-01 14:43:56

物聯(lián)網(wǎng)智慧城市

2020-04-06 14:45:22

云計算邊緣計算網(wǎng)絡(luò)

2011-03-09 17:20:43

SSL VPNVPN

2025-06-24 02:00:00

5G-A運營商基站

2019-08-05 08:42:37

物聯(lián)網(wǎng)IOT技術(shù)
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 国产一级片免费视频 | 国产一区二区自拍 | 中文字幕一区二区三区四区 | 国产精品1区2区 | 欧美群妇大交群中文字幕 | 日韩精品国产精品 | av在线播放网 | 亚洲乱码一区二区三区在线观看 | 久久国产香蕉 | 99爱在线视频 | www.欧美视频| 午夜久久久久久久久久一区二区 | 美人の美乳で授乳プレイ | 久久精品欧美电影 | 视频一区在线观看 | 人人射人人插 | 亚洲视频在线免费观看 | 国产精品久久久久久久久久久久久 | 亚洲一区二区三区免费在线观看 | 美女人人操 | 欧美精品一区二区蜜桃 | 久草精品视频 | 欧美一级二级在线观看 | 国产日韩一区二区三免费高清 | 国产在线高清 | 精品久久中文 | 国产激情免费视频 | 成人av网站在线观看 | 九一精品 | 国产欧美在线播放 | 精品久久久久久久久久久久久久久久久 | 欧美日韩免费一区二区三区 | 国产一区 | 99久久久久久久 | 天天干天天爱天天爽 | 免费网站国产 | 国产91在线播放 | 久久99深爱久久99精品 | 日韩在线一区二区三区 | 99热欧美 | 国产成人精品久久二区二区 |