您好,登錄后才能下訂單哦!
這篇文章主要介紹了Rust中FFI編程知識點有哪些的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Rust中FFI編程知識點有哪些文章都會有所收獲,下面我們一起來看看吧。
Rust 語言主要在關鍵字和標準庫兩個方面對 FFI 提供了支持,具體如下:
關鍵字 extern
屬性 #[no_mangle]
外部塊 ExternBlock 及其屬性 link 和 link_name
標準庫
std:os:raw 模塊:例如c_char。
std:ffi 模塊:傳遞 UTF-8 字符串時,CString和CStr很有用。
你可以使用 libc::foo 這種形式訪問這個庫中的任何導出內容。
在Rust里,只能創建子線程,如果想創建子進程,就需要用到libc庫
fn main() { unsafe { let pid = libc::fork(); if pid > 0 {println!("Hello, I am parent thread: {}", libc::getpid());} else if pid == 0 {println!("Hello, I am child thread: {}", libc::getpid());println!("My parent thread: {}", libc::getppid()); } else {println!("Fork creation failed!");}}}
1.libc 的所有函數調用,都必須放進 unsafe 塊中。因為它的所有調用都是 unsafe 的;
2.std 的線程操作封裝,好用,形象。libc 的進程操作,與 C 語言系統編程一樣,完全是另外一套思路和編程風格;
3.std 的線程操作雖然簡潔,但是也缺少更細顆粒度的控制。而 libc 可以對進程的操作(及后面對子進程的功能擴充,父進程中的信號管理等),做到完全的控制,更加靈活,功能強大;
4.std 本身無法實現進程 fork 的功能。
因為我 Rust 的封裝是 zero cost (零成本)的。零成本抽象賦予了 Rust 系統編程的能力。
libc 與 std::os:????::raw,這里面有的用法是一樣的,沒有任何問題。簡單的和C交互可以用os:raw里面的,而一旦產生了系統調用或者 Unix 環境編程,那么就得引入 libc 庫來操作。
這個工具就是將寫好的Rust代碼配置一下,然后會自動生成接口代碼頭文件等等。其實,FFI封裝、轉換,熟悉了之后,知識點就那些,模式也比較固定,如果接口量很大,那就需要大量重復的 coding。量一大,人手動綁定出錯的機率也大。所以這種輔助工具的意義就顯露出來了。基于輔助工具生成的代碼,如不完美,再適當手動修一修,幾下就能搞定,大大提高生產效率。
在Rust中,存在三種類型的指針:
引用—安全的指針
&T:它是對類型T的不可變引用
&mut T:它是對類型T的可變引用
眾所周知,Rust語言的指針是一種安全的指針,它會遵循一定的規則,比如ownership規則,會確保不出現懸掛指針。但是當我們需要寫一些底層框架的時候,往往需要繞過這些規則,自由的控制指針,這時候我們就可以使用原始指針。
*const T:表示指向類型T的不可變原始指針。它是Copy類型。這類似于&T,只是它可以為空值。
*mut T:一個指向T的可變原始指針,它不支持Copy特征(non-Copy)。
以下可以定義Rust的原始指針:
fn main() { let mut num = 5; let r1 = &num as *const i32; let r2 = &mut num as *mut i32; }
管理原始指針非常不安全,開發者在使用它們時需要注意很多細節。不恰當地使用它們可能會以非常隱蔽的方式導致諸如內存泄漏、引用掛起,以及大型代碼庫中的雙重釋放等問題。為了解決這些問題,我們可以使用C++中廣泛采用的智能指針。
智能指針的兩個特性:Drop和Deref
Drop:這是多次提及的特征,它可以自動釋放相關值超出作用域后占用的資源。Drop特征類似于你在其他語言中遇到的被稱為對象析構函數的東西。它包含一個drop方法,當對象超出作用域時就會被調用。該方法將&mut self作為參數。使用drop釋放值是以LIFO的方式進行的。也就是說,無論最后構建的是什么,都首先會被銷毀。drop方法是你為自己的結構體放置清理代碼的理想場所。例如使用引用計數值或GC時,它尤其方便。當我們實例化任何Drop實現值時(任意堆分配類型),Rust編譯器會在編譯后的代碼中每個作用域結束的位置插入drop方法調用。因此,我們不需要在這些實例上手動調用drop方法。
Deref:為了提供與普通指針類似的行為,也就是說,為了能夠解引用被指向類型的調用方法,智能指針類型通常會實現Deref特征,這允許用戶對這些類型使用解引用運算符*。雖然Deref只為你提供了只讀權限,但是還有DerefMut,它可以為你提供對底層類型的可變引用。
智能指針的種類:
標準庫中的智能指針有如下幾種。
Box:它提供了最簡單的堆資源分配方式。Box類型擁有其中的值,并且可用于保存結構體中的值,或者從函數返回它們。
Rc:它用于引用計數。每當獲取新引用時,計數器會執行遞增操作,并在用戶釋放引用時對計數器執行遞減操作。當計數器的值為零時,該值將被移除。
Arc:它用于原子引用計數。這與之前的類型類似,但具有原子性以保證多線程的安全性。
Cell:它為我們提供實現了Copy特征的類型的內部可變性。換句話說,我們有可能獲得多個可變引用。
RefCell:它為我們提供了類型的內部可變性,并且不需要實現Copy特征。它用于運行時的鎖定以確保安全性。
引用計數指針:
所有權規則只允許某個給定作用域中存在一個所有者。但是,在某些情況下你需要與多個變量共享類型。例如在GUI庫中,每個子窗體小部件都需要具有對其父容器窗口小部件的引用,以便基于用戶的resize事件來調整子窗口的布局。雖然有時生命周期允許你將父節點存儲為&'a Parent,但是它通常受到’a值生命周期的限制,一旦作用域結束,你的引用將失效。在這種情況下,我們需要更靈活的方法,并且需要使用引用計數類型。程序中的這些智能指針類型會提供值的共享所有權。
引用計數類型支持某個粒度級別的垃圾回收。在這種方法中,智能指針類型允許用戶對包裝值進行多次引用。在內部,智能指針使用引用計數器(這里是refcount)來統計已發放的并且活動的引用數量,不過它只是一個整數值。當引用包裝的智能指針值的變量超出作用域時,refcount的值就會遞減。一旦該對象的所有引用都消失,refcount的值也會變成0,之后該值會被銷毀。這就是引用計數指針的常見工作模式。
Rust為我們提供了兩種引用計數指針類型。
Rc:這主要用于單線程環境。
Arc:這主要用于多線程環境。
1.pub extern “C” fn sum_of_array(array: *const u32, len: usize) -> u32
slice::from_raw_parts(array,len)
C端傳來的數組(指針類型),進到Rust這邊進行強制類型轉換,變成非可變原始指針類型。函數slice::from_raw_parts(array,len)就是對原始指針進行轉換為Rust切片類型,切片就是一個指針+一個長度即可。
2.CStr::from_ptr(raw_string):CStr就是C端產生數據,Rust端使用,
只是借用,常用于打印。raw_string是直接從C接過來的可變原始指針。
使用std::ffi::CStr提供的from_ptr方法包裝 C 的字符串指針,它基于空字符’\0’來計算字符串的長度,并可以通過它將外部 C 字符串轉換為 Rust 的 &str和String
use std::ffi::CStr; use libc::c_char; extern { fn char_func() -> *mut c_char; } fn get_string() -> String { unsafe { let raw_string: *mut c_char = char_func(); let cstr = CStr::from_ptr(raw_string); cstr.to_string_lossy().into_owned() } }
3.CStr::from_ptr(s).to_string_lossy().into_owned():注意to_string_lossy()的使用:因為在rust中一切字符都是采用utf8表示的而c不是,
因此如果要將c的字符串轉換到rust字符串的話,需要檢查是否都為有效utf-8字節。
4.CString::new(“Hello, world!”).as_ptr():Cstring是Rust端產生數據,C端進行使用。
as_ptr()就是將RustCString指針類型轉化為C的原始指針類型。
5.CString::new(“Hello world!”).into_raw()
使用std::ffi::CString提供的一對方法into_raw和from_raw可以進行原始指針轉換,由于將字符串的所有權轉移給了調用者,所以調用者必須將字符串返回給 Rust,以便正確地釋放內存。
into_raw()和.as_ptr()的作用類似,都是變成原始指針傳給C端。
6.CString::from_raw(s)
一般在釋放內存的時候使用,C端用完需要Rust端來釋放。
7.Box::into_raw(Box::new(new_stu)):其實這里是智能指針和兩端堆棧申請有關,into_raw()就是將Rust智能指針變成原始指針。
8.Box::from_raw(p_stu):from_raw():就是將C端傳來的p_stu變成Rust智能指針。
C代碼:
uint32_t sum = sum_of_array(numbers, length);
Rust代碼:
pub extern "C" fn sum_of_array(array: *const u32, len: usize) -> u32 { let array = unsafe { assert!(!array.is_null()); slice::from_raw_parts(array, len) }; array.iter().sum() }
這里的參數傳遞一目了然,array一開始是C過來的指針類型,通過slice::from_raw_parts(array,len)之后,變成一個Rust切片類型,后面用iter進行求和。切片類型就是一個指針和一組數據合在一起組成。
對于C語言來說,字符串有兩種,一種是共享的只讀字符串 char * ,不能修改。另一種是動態分配的可變字符串 char [],可以修改。
而在Rust里面,字符串是由字符的 UTF-8 編碼組成的字節序列。表示的類型有很多種。
字符串則比較復雜,Rust 中的字符串,是一組u8組成的 UTF-8 編碼的字節序列,字符串內部允許NULL字節;但在 C 中,字符串只是指向一個char的指針,用一個NULL字節作為終止。
我們需要做一些特殊的轉換,在 Rust FFI 中使用std::ffi::CStr,它表示一個NULL字節作為終止的字節數組,可以通過 UTF-8 驗證轉換成 Rust 中的&str。
CStr:表示以空字符終止的 C 字符串或字節數組的借用,屬于引用類型。一般用于和 C 語言交互,由 C 分配并被 Rust 借用的字符串。
CString:表示擁有所有權的,中間沒有空字節,以空字符終止的字符串類型。一般用于和 C 語言交互時,由 Rust 分配并傳遞給 C 的字符串。
下面這段代碼,在這里get_string使用CStr::from_ptr從C的char*獲取一個字符串,并且轉化成了一個String。
fn get_string() -> String { unsafe { let raw_string: *mut c_char = char_func(); let cstr = CStr::from_ptr(raw_string); cstr.to_string_lossy().into_owned() } }
和CStr表示從C中來,rust不擁有歸屬權的字符串相反,CString表示由rust分配,Rust擁有所有權,可以進行修改,用以傳給C程序的字符串。
use std::ffi::CString; use std::os::raw::c_char; extern { fn my_printer(s: *const c_char); } let c_to_print = CString::new("Hello, world!").unwrap(); unsafe { my_printer(c_to_print.as_ptr()); // 使用 as_ptr 將CString轉化成char指針傳給c函數 }
兩端分配堆棧,另一端填充打印
關于“Rust中FFI編程知識點有哪些”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Rust中FFI編程知識點有哪些”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。