Go調(diào)用Rust方法及外部函數(shù)接口前置
前言
近期 Rust 社區(qū)/團隊有些變動,所以再一次將 Rust 拉到大多數(shù)人眼前。
我最近看到很多小伙伴說的話:
- Rust 還值得學(xué)嗎?社區(qū)是不是不穩(wěn)定呀
- Rust 和 Go 哪個好?
- Rust 還值得學(xué)嗎?
這些問題如果有人來問我,那我的回答是:
小孩子才做選擇,我都要!
當然,關(guān)于 Rust 和 Go 的問題也不算新,比如之前的一條推文:
我在本篇中就來介紹下如何用 Go 調(diào)用 Rust。
當然,這篇中我基本上不會去比較 Go 和 Rust 的功能,或者這種方式的性能之類的,Just for Fun
FFI 和 Binding
FFI (Foreign Function Interface) 翻譯過來叫做外部函數(shù)接口(為了比較簡單,下文中都將使用 FFI 指代)。最早來自于 Common Lisp 的規(guī)范,這是在 wiki 上寫的,我并沒有去考證。 不過我所使用過的絕大多數(shù)語言中都有 FFI 的概念/術(shù)語存在,比如:Python、Ruby, Haskell、Go、Rust、LuaJIT 等。
FFI 的作用簡單來說就是允許一種語言去調(diào)用另一種語言,有時候我們也會用 Binding 來表示類似的能力。
在不同的語言中會有不同的實現(xiàn),比如在 Go 中的 cgo , Python 中的 ctypes , Haskell 中的 CAPI (之前還有一個 ccall)等。 我個人感覺 Haskell 中用 FFI 相比其他語言要更簡單&方便的多,不過這不是本篇的重點就不展開了。
在本文中,對于 Go 和 Rust 而言,它們的 FFI 需要與 C 語言對象進行通信,而這部分其實是由操作系統(tǒng)根據(jù) API 中的調(diào)用約定來完成的。
我們來進入正題。
準備 Rust 示例程序
Rust 的安裝和 Cargo 工具的基本使用,這里就不介紹了。大家可以去 Rust 的官網(wǎng)進行了解。
用 Cargo 創(chuàng)建項目
我們先準備一個目錄用來放本次示例的代碼。(我創(chuàng)建的目錄叫做 go-rust )
然后使用 Rust 的 Cargo 工具創(chuàng)建一個名叫 rustdemo 的項目,這里由于我增加了 --lib 的選項,使用其內(nèi)置的 library 模板。
? go-rust git:(master) ? mkdir lib && cd lib ? go-rust git:(master) ? cargo new --lib rustdemo Created library `rustdemo` package ? go-rust git:(master) ? tree rustdemo rustdemo ├── Cargo.toml └── src └── lib.rs 1 directory, 2 files
準備 Rust 代碼
extern crate libc; use std::ffi::{CStr, CString}; #[no_mangle] pub extern "C" fn rustdemo(name: *const libc::c_char) -> *const libc::c_char { let cstr_name = unsafe { CStr::from_ptr(name) }; let mut str_name = cstr_name.to_str().unwrap().to_string(); println!("Rust get Input: \"{}\"", str_name); let r_string: &str = " Rust say: Hello Go "; str_name.push_str(r_string); CString::new(str_name).unwrap().into_raw() }
代碼比較簡單,Rust 暴露出來的函數(shù)名叫做 rustdemo ,接收一個外部的參數(shù),并將其打印出來。之后從 Rust 這邊再設(shè)置一個字符串。
CString::new(str_name).unwrap().into_raw() 被轉(zhuǎn)換為原始指針,以便之后由 C 語言處理。
編譯 Rust 代碼
我們需要修改下 Cargo.toml 文件以便進行編譯。注意,這里我們增加了 crate-type = ["cdylib"] 和 libc 。
[package] name = "rustdemo" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"] [dependencies] libc = "0.2"
然后進行編譯
? rustdemo git:(master) ? cargo build --release Compiling rustdemo v0.1.0 (/home/tao/go/src/github.com/tao12345666333/go-rust/lib/rustdemo) Finished release [optimized] target(s) in 0.22s
查看生成的文件,這是一個 .so 文件(這是因為我在 Linux 環(huán)境下,你如果在其他系統(tǒng)環(huán)境下會不同)
? rustdemo git:(master) ? ls target/release/librustdemo.so target/release/librustdemo.so
準備 Go 代碼
Go 環(huán)境的安裝之類的這里也不再贅述了,繼續(xù)在我們的 go-rust 目錄操作即可。
編寫 main.go
package main /* #cgo LDFLAGS: -L./lib -lrustdemo #include <stdlib.h> #include "./lib/rustdemo.h" */ import "C" import ( "fmt" "unsafe" ) func main() { s := "Go say: Hello Rust" input := C.CString(s) defer C.free(unsafe.Pointer(input)) o := C.rustdemo(input) output := C.GoString(o) fmt.Printf("%s\n", output) }
在這里我們使用了 cgo ,在 import "C" 之前的注釋內(nèi)容是一種特殊的語法,這里是正常的 C 代碼,其中需要聲明使用到的頭文件之類的。
下面的代碼很簡單,定義了一個字符串,傳遞給 rustdemo 函數(shù),然后打印 C 處理后的字符串。
同時,為了能夠讓 Go 程序能正常調(diào)用 Rust 函數(shù),這里我們還需要聲明其頭文件,在 lib/rustdemo.h 中寫入如下內(nèi)容:
char* rustdemo(char *name);
編譯代碼
在 Go 編譯的時候,我們需要開啟 CGO (默認都是開啟的),同時需要鏈接到 Rust 構(gòu)建出來的 rustdemo.so 文件,所以我們將該文件和它的頭文件放到 lib 目錄下。
? go-rust git:(master) ? cp lib/rustdemo/target/release/librustdemo.so lib
所以完整的目錄結(jié)構(gòu)就是:
? go-rust git:(master) ? tree -L 2 . . ├── go.mod ├── lib │ ├── librustdemo.so │ ├── rustdemo │ └── rustdemo.h └── main.go 2 directories, 5 files
編譯:
? go-rust git:(master) ? go build -o go-rust -ldflags="-r ./lib" main.go ? go-rust git:(master) ? ./go-rust Rust get Input: "Go say: Hello Rust" Go say: Hello Rust Rust say: Hello Go
可以看到,第一行的輸出是由 Go 傳入了 Rust , 第二行中則是從 Rust 再傳回 Go 的了。符合我們的預(yù)期。
總結(jié)
本篇介紹了如何使用 Go 與 Rust 進行結(jié)合,介紹了其前置關(guān)于 FFI 相關(guān)的知識,后續(xù)通過一個小的實踐演示了其完整過程。 感興趣的小伙伴可以自行實踐下,更多關(guān)于Go調(diào)用Rust外部函數(shù)接口前置的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Rust使用Channel實現(xiàn)跨線程傳遞數(shù)據(jù)
消息傳遞是一種很流行且能保證安全并發(fā)的技術(shù),Rust也提供了一種基于消息傳遞的并發(fā)方式,在rust里使用標準庫提供的Channel來實現(xiàn),下面我們就來學(xué)習(xí)一下如何使用Channel實現(xiàn)跨線程傳遞數(shù)據(jù)吧2023-12-12Rust?連接?PostgreSQL?數(shù)據(jù)庫的詳細過程
這篇文章主要介紹了Rust?連接?PostgreSQL?數(shù)據(jù)庫的完整代碼,本文圖文實例代碼相結(jié)合給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-01-01C和Java沒那么香了,Serverless時代Rust即將稱王?
Serverless Computing,即”無服務(wù)器計算”,其實這一概念在剛剛提出的時候并沒有獲得太多的關(guān)注,直到2014年AWS Lambda這一里程碑式的產(chǎn)品出現(xiàn)。Serverless算是正式走進了云計算的舞臺2021-06-06