Rust語(yǔ)言中級(jí)教程之指針
Rust語(yǔ)言中級(jí)教程
一、指針
什么是指針
- 指針是計(jì)算機(jī)引用無法立即直接訪問的數(shù)據(jù)的一種方式(類比 書的目錄)
- 數(shù)據(jù)在物理內(nèi)存(RAM)中是分散的存儲(chǔ)著
- 地址空間是檢索系統(tǒng)
- 指針就被編碼為內(nèi)存地址,使用 usize 類型的整數(shù)表示。
- 一個(gè)地址就會(huì)指向地址空間中的某個(gè)地方
- 地址空間的范圍是 OS 和 CPU 提供的外觀界面
- 程序只知道有序的字節(jié)序列,不會(huì)考慮系統(tǒng)中實(shí)際 RAM 的數(shù)量
名詞解釋
- 內(nèi)存地址(地址),就是指代內(nèi)存中單個(gè)字節(jié)的一個(gè)數(shù)
- 內(nèi)存地址是匯編語(yǔ)言提供的抽象
- 指針(有時(shí)擴(kuò)展稱為原始指針),就是指向某種類型的一個(gè)內(nèi)存地址
- 指針是高級(jí)語(yǔ)言提供的抽象
- 引用,就是指針。如果是動(dòng)態(tài)大小的類型,就是指針和具有額外保證的一個(gè)整數(shù)
- 引用是 Rust 提供的抽象
Rust 的引用
- 引用始終引用的是有效數(shù)據(jù)
- 引用與 usize 的倍數(shù)對(duì)齊
- 引用可以為動(dòng)態(tài)大小的類型提供上述保障
Rust 的引用 和 指針
static B: [u8; 10] = [99, 97, 114, 114, 121, 116, 111, 119, 101, 108]; static C: [u8; 11] = [116, 104, 97, 110, 107, 115, 102, 105, 115, 104, 0]; fn main() { let a = 42; let b = &B; let c = &C; println!("a: {}, b: {:p}, c: {:p}", a, b, c); }
運(yùn)行
point_demo on master [?] is ?? 0.1.0 via ?? 1.67.1 via ?? base
? cargo run
Compiling point_demo v0.1.0 (/Users/qiaopengjun/rust/point_demo)
Finished dev [unoptimized + debuginfo] target(s) in 0.44s
Running `target/debug/point_demo`
a: 42, b: 0x1023dc660, c: 0x1023dc66apoint_demo on master [?] is ?? 0.1.0 via ?? 1.67.1 via ?? base
?
- 一個(gè)更加逼真的例子
- 使用更復(fù)雜的類型展示指針內(nèi)部的區(qū)別
use std::mem::size_of; static B: [u8; 10] = [99, 97, 114, 114, 121, 116, 111, 119, 101, 108]; static C: [u8; 11] = [116, 104, 97, 110, 107, 115, 102, 105, 115, 104, 0]; fn main() { // let a = 42; // let b = &B; // let c = &C; // println!("a: {}, b: {:p}, c: {:p}", a, b, c); let a: usize = 42; let b: Box<[u8]> = Box::new(B); let c: &[u8; 11] = &C; println!("a (unsigned 整數(shù)):"); println!(" 地址: {:p}", &a); println!(" 大小: {:?} bytes", size_of::<usize>()); println!(" 值: {:?}\n", a); println!("b (B 裝在 Box 里):"); println!(" 地址: {:p}", &b); println!(" 大小: {:?} bytes", size_of::<Box<[u8]>>()); println!(" 指向: {:p}\n", b); println!("c (C 的引用):"); println!(" 地址: {:p}", &c); println!(" 大小: {:?} bytes", size_of::<&[u8; 11]>()); println!(" 指向: {:p}\n", c); println!("B (10 bytes 的數(shù)組):"); println!(" 地址: {:p}", &B); println!(" 大小: {:?} bytes", size_of::<[u8; 10]>()); println!(" 值: {:?}\n", B); println!("C (11 bytes 的數(shù)字):"); println!(" 地址: {:p}", &C); println!(" 大小: {:?} bytes", size_of::<[u8; 11]>()); println!(" 值: {:?}\n", C); }
運(yùn)行
point_demo on master [?] is ?? 0.1.0 via ?? 1.67.1 via ?? base
? cargo run
Compiling point_demo v0.1.0 (/Users/qiaopengjun/rust/point_demo)
Finished dev [unoptimized + debuginfo] target(s) in 0.19s
Running `target/debug/point_demo`
a (unsigned 整數(shù)):
地址: 0x16dda9a08
大小: 8 bytes
值: 42b (B 裝在 Box 里):
地址: 0x16dda9a10
大小: 16 bytes
指向: 0x12b606ba0c (C 的引用):
地址: 0x16dda9a30
大小: 8 bytes
指向: 0x10208d7baB (10 bytes 的數(shù)組):
地址: 0x10208d7b0
大小: 10 bytes
值: [99, 97, 114, 114, 121, 116, 111, 119, 101, 108]C (11 bytes 的數(shù)字):
地址: 0x10208d7ba
大小: 11 bytes
值: [116, 104, 97, 110, 107, 115, 102, 105, 115, 104, 0]point_demo on master [?] is ?? 0.1.0 via ?? 1.67.1 via ?? base
?
- 對(duì) B 和 C 中文本進(jìn)行解碼的例子
- 它創(chuàng)建了一個(gè)與前圖更加相似的內(nèi)存地址布局
use std::borrow::Cow; use std::ffi::CStr; use std::os::raw::c_char; static B: [u8; 10] = [99, 97, 114, 114, 121, 116, 111, 119, 101, 108]; static C: [u8; 11] = [116, 104, 97, 110, 107, 115, 102, 105, 115, 104, 0]; fn main() { let a = 42; let b: String; let c: Cow<str>; unsafe { let b_ptr = &B as * const u8 as *mut u8; b = String::from_raw_parts(b_ptr, 10, 10); let c_ptr = &C as *const u8 as *const c_char; c = CStr::from_ptr(c_ptr).to_string_lossy(); } println!("a: {}, b: {}, c: {}", a, b, c); }
Raw Pointers(原始指針)
- Raw Pointer (原始指針)是沒有 Rust 標(biāo)準(zhǔn)保障的內(nèi)存地址。
- 這些本質(zhì)上是 unsafe 的
- 語(yǔ)法:
- 不可變 Raw Pointer:*const T
- 可變的 Raw Pointer:*mut T
- 注意:*const T,這三個(gè)標(biāo)記放在一起表示的是一個(gè)類型
- 例子:*const String
- *const T 與 *mut T 之間的差異很小,相互可以自由轉(zhuǎn)換
- Rust 的引用(&mut T 和 &T)會(huì)編譯為原始指針
- 這意味著無需冒險(xiǎn)進(jìn)入 unsafe 塊,就可以獲得原始指針的性能
- 例子:把引用轉(zhuǎn)為原始指針
fn main() { let a: i64 = 42; let a_ptr = &a as *const i64; println!("a: {} ({:p})", a, a_ptr); }
- 解引用(dereference):通過指針從 RAM 內(nèi)存提取數(shù)據(jù)的過程叫做對(duì)指針進(jìn)行解引用(dereferencing a pointer)
- 例子:把引用轉(zhuǎn)為原始指針
fn main() { let a: i64 = 42; let a_ptr = &a as *const i64; let a_addr: usize = unsafe {std::mem::transmute(a_ptr)}; println!("a: {} ({:p}...0x{:x})", a, a_ptr, a_addr + 7); }
關(guān)于 Raw Pointer 的提醒
- 在底層,引用(&T 和 &mutT)被實(shí)現(xiàn)為原始指針。但引用帶有額外的保障,應(yīng)該始終作為首選使用
- 訪問 Raw Pointer 的值總是 unsafe 的
- Raw Pointer 不擁有值的所有權(quán)
- 在訪問時(shí)編譯器不會(huì)檢查數(shù)據(jù)的合法性
- 允許多個(gè) Raw Pointer 指向同一數(shù)據(jù)
- Rust 無法保證共享數(shù)據(jù)的合法性
使用 Raw Pointer 的情況
- 不可避免
- 某些 OS 或 第三方庫(kù)需要使用,例如與C交互
- 共享對(duì)某些內(nèi)容的訪問至關(guān)重要,運(yùn)行時(shí)性能要求高
Rust 指針生態(tài)
- Raw Pointer 是 unsafe 的
- Smart Pointer(智能指針)傾向于包裝原始指針,附加更多的能力
- 不僅僅是對(duì)內(nèi)存地址解引用
Rust 智能指針
名稱 | 簡(jiǎn)介 | 強(qiáng)項(xiàng) | 弱項(xiàng) |
---|---|---|---|
Raw Pointer | *mut T 和 *const T,自由基,閃電般塊,極其 Unsafe | 速度、與外界交互 | Unsafe |
Box<T> | 可把任何東西都放在Box里。可接受幾乎任何類型的長(zhǎng)期存儲(chǔ)。新的安全編程時(shí)代的主力軍。 | 將值集中存儲(chǔ)在 Heap | 大小增加 |
Rc<T> | 是Rust的能干而吝嗇的簿記員。它知道誰(shuí)借了什么,何時(shí)借了什么 | 對(duì)值的共享訪問 | 大小增加;運(yùn)行時(shí)成本;線程不安全 |
Arc<T> | 是Rust的大使。它可以跨線程共享值,保證這些值不會(huì)相互干擾 | 對(duì)值的共享訪問;線程安全 | 大小增加;運(yùn)行時(shí)成本 |
Cell<T> | 變態(tài)專家,具有改變不可變值的能力 | 內(nèi)部可變性 | 大小增加;性能 |
RefCell<T> | 對(duì)不可變引用執(zhí)行改變,但有代價(jià) | 內(nèi)部可變性;可與僅接受不可變引用的Rc、Arc嵌套使用 | 大小增加;運(yùn)行時(shí)成本;缺乏編譯時(shí)保障 |
Cow<T> | 封閉并提供對(duì)借用數(shù)據(jù)的不可變?cè)L問,并在需要修改或所有權(quán)時(shí)延遲克隆數(shù)據(jù) | 當(dāng)只是只讀訪問時(shí)避免寫入 | 大小可能會(huì)增大 |
String | 可處理可變長(zhǎng)度的文本,展示了如何構(gòu)建安全的抽象 | 動(dòng)態(tài)按需增長(zhǎng);在運(yùn)行時(shí)保證正確編碼 | 過度分配內(nèi)存大小 |
Vec<T> | 程序最常用的存儲(chǔ)系統(tǒng);它在創(chuàng)建和銷毀值時(shí)保持?jǐn)?shù)據(jù)有序 | 動(dòng)態(tài)按需增長(zhǎng) | 過度分配內(nèi)存大小 |
RawVec<T> | 是Vec<T> 和其它動(dòng)態(tài)大小類型的基石;知道如何按需給你的數(shù)據(jù)提供一個(gè)家 | 動(dòng)態(tài)按需增長(zhǎng);與內(nèi)存分配器一起配合尋找空間 | 不直接適用于您的代碼 |
Unique<T> | 作為值的唯一所有者,可保證擁有完全控制權(quán) | 需要獨(dú)占值的類型(如 String)的基礎(chǔ) | 不適合直接用于應(yīng)用程序代碼 |
Shared<T> | 分享所有權(quán)很難,但他使生活更輕松 | 共享所有權(quán);可以將內(nèi)存與T的寬度對(duì)齊,即使是空的時(shí)候 | 不適合直接用于應(yīng)用程序代碼 |
到此這篇關(guān)于Rust語(yǔ)言中級(jí)教程之指針的文章就介紹到這了,更多相關(guān)Rust指針內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust?use關(guān)鍵字妙用及模塊內(nèi)容拆分方法
這篇文章主要介紹了Rust?use關(guān)鍵字妙用|模塊內(nèi)容拆分,文中還給大家介紹use關(guān)鍵字的習(xí)慣用法,快速引用自定義模塊內(nèi)容或標(biāo)準(zhǔn)庫(kù),以此優(yōu)化代碼書寫,需要的朋友可以參考下2022-09-09Rust語(yǔ)言之Prometheus系統(tǒng)監(jiān)控工具包的使用詳解
Prometheus?是一個(gè)開源的系統(tǒng)監(jiān)控和警報(bào)工具包,最初是由SoundCloud構(gòu)建的,隨著時(shí)間的發(fā)展,Prometheus已經(jīng)具有適用于各種使用場(chǎng)景的版本,為了開發(fā)者方便開發(fā),更是有各種語(yǔ)言版本的Prometheus的開發(fā)工具包,本文主要介紹Rust版本的Prometheus開發(fā)工具包2023-10-10關(guān)于Rust?使用?dotenv?來設(shè)置環(huán)境變量的問題
在項(xiàng)目中,我們通常需要設(shè)置一些環(huán)境變量,用來保存一些憑證或其它數(shù)據(jù),這時(shí)我們可以使用dotenv這個(gè)crate,接下來通過本文給大家介紹Rust?使用dotenv來設(shè)置環(huán)境變量的問題,感興趣的朋友一起看看吧2022-01-01