Go調(diào)用Rust方法及外部函數(shù)接口前置
前言
近期 Rust 社區(qū)/團(tuán)隊(duì)有些變動(dòng),所以再一次將 Rust 拉到大多數(shù)人眼前。
我最近看到很多小伙伴說(shuō)的話:
- Rust 還值得學(xué)嗎?社區(qū)是不是不穩(wěn)定呀
- Rust 和 Go 哪個(gè)好?
- Rust 還值得學(xué)嗎?
這些問(wèn)題如果有人來(lái)問(wèn)我,那我的回答是:
小孩子才做選擇,我都要!
當(dāng)然,關(guān)于 Rust 和 Go 的問(wèn)題也不算新,比如之前的一條推文:
我在本篇中就來(lái)介紹下如何用 Go 調(diào)用 Rust。
當(dāng)然,這篇中我基本上不會(huì)去比較 Go 和 Rust 的功能,或者這種方式的性能之類的,Just for Fun
FFI 和 Binding
FFI (Foreign Function Interface) 翻譯過(guò)來(lái)叫做外部函數(shù)接口(為了比較簡(jiǎn)單,下文中都將使用 FFI 指代)。最早來(lái)自于 Common Lisp 的規(guī)范,這是在 wiki 上寫的,我并沒有去考證。 不過(guò)我所使用過(guò)的絕大多數(shù)語(yǔ)言中都有 FFI 的概念/術(shù)語(yǔ)存在,比如:Python、Ruby, Haskell、Go、Rust、LuaJIT 等。
FFI 的作用簡(jiǎn)單來(lái)說(shuō)就是允許一種語(yǔ)言去調(diào)用另一種語(yǔ)言,有時(shí)候我們也會(huì)用 Binding 來(lái)表示類似的能力。
在不同的語(yǔ)言中會(huì)有不同的實(shí)現(xiàn),比如在 Go 中的 cgo , Python 中的 ctypes , Haskell 中的 CAPI (之前還有一個(gè) ccall)等。 我個(gè)人感覺 Haskell 中用 FFI 相比其他語(yǔ)言要更簡(jiǎn)單&方便的多,不過(guò)這不是本篇的重點(diǎn)就不展開了。
在本文中,對(duì)于 Go 和 Rust 而言,它們的 FFI 需要與 C 語(yǔ)言對(duì)象進(jìn)行通信,而這部分其實(shí)是由操作系統(tǒng)根據(jù) API 中的調(diào)用約定來(lái)完成的。
我們來(lái)進(jìn)入正題。
準(zhǔn)備 Rust 示例程序
Rust 的安裝和 Cargo 工具的基本使用,這里就不介紹了。大家可以去 Rust 的官網(wǎng)進(jìn)行了解。
用 Cargo 創(chuàng)建項(xiàng)目
我們先準(zhǔn)備一個(gè)目錄用來(lái)放本次示例的代碼。(我創(chuàng)建的目錄叫做 go-rust )
然后使用 Rust 的 Cargo 工具創(chuàng)建一個(gè)名叫 rustdemo 的項(xiàng)目,這里由于我增加了 --lib 的選項(xiàng),使用其內(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
準(zhǔn)備 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() }
代碼比較簡(jiǎn)單,Rust 暴露出來(lái)的函數(shù)名叫做 rustdemo ,接收一個(gè)外部的參數(shù),并將其打印出來(lái)。之后從 Rust 這邊再設(shè)置一個(gè)字符串。
CString::new(str_name).unwrap().into_raw() 被轉(zhuǎn)換為原始指針,以便之后由 C 語(yǔ)言處理。
編譯 Rust 代碼
我們需要修改下 Cargo.toml 文件以便進(jìn)行編譯。注意,這里我們?cè)黾恿?crate-type = ["cdylib"] 和 libc 。
[package] name = "rustdemo" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"] [dependencies] libc = "0.2"
然后進(jìn)行編譯
? 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
查看生成的文件,這是一個(gè) .so 文件(這是因?yàn)槲以?Linux 環(huán)境下,你如果在其他系統(tǒng)環(huán)境下會(huì)不同)
? rustdemo git:(master) ? ls target/release/librustdemo.so target/release/librustdemo.so
準(zhǔn)備 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)容是一種特殊的語(yǔ)法,這里是正常的 C 代碼,其中需要聲明使用到的頭文件之類的。
下面的代碼很簡(jiǎn)單,定義了一個(gè)字符串,傳遞給 rustdemo 函數(shù),然后打印 C 處理后的字符串。
同時(shí),為了能夠讓 Go 程序能正常調(diào)用 Rust 函數(shù),這里我們還需要聲明其頭文件,在 lib/rustdemo.h 中寫入如下內(nèi)容:
char* rustdemo(char *name);
編譯代碼
在 Go 編譯的時(shí)候,我們需要開啟 CGO (默認(rèn)都是開啟的),同時(shí)需要鏈接到 Rust 構(gòu)建出來(lái)的 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 進(jìn)行結(jié)合,介紹了其前置關(guān)于 FFI 相關(guān)的知識(shí),后續(xù)通過(guò)一個(gè)小的實(shí)踐演示了其完整過(guò)程。 感興趣的小伙伴可以自行實(shí)踐下,更多關(guān)于Go調(diào)用Rust外部函數(shù)接口前置的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Rust使用Channel實(shí)現(xiàn)跨線程傳遞數(shù)據(jù)
消息傳遞是一種很流行且能保證安全并發(fā)的技術(shù),Rust也提供了一種基于消息傳遞的并發(fā)方式,在rust里使用標(biāo)準(zhǔn)庫(kù)提供的Channel來(lái)實(shí)現(xiàn),下面我們就來(lái)學(xué)習(xí)一下如何使用Channel實(shí)現(xiàn)跨線程傳遞數(shù)據(jù)吧2023-12-12Rust?連接?PostgreSQL?數(shù)據(jù)庫(kù)的詳細(xì)過(guò)程
這篇文章主要介紹了Rust?連接?PostgreSQL?數(shù)據(jù)庫(kù)的完整代碼,本文圖文實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01Rust中FFI編程知識(shí)點(diǎn)整理總結(jié)(推薦)
這篇文章主要介紹了Rust中FFI編程知識(shí)點(diǎn)整理總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09C和Java沒那么香了,Serverless時(shí)代Rust即將稱王?
Serverless Computing,即”無(wú)服務(wù)器計(jì)算”,其實(shí)這一概念在剛剛提出的時(shí)候并沒有獲得太多的關(guān)注,直到2014年AWS Lambda這一里程碑式的產(chǎn)品出現(xiàn)。Serverless算是正式走進(jìn)了云計(jì)算的舞臺(tái)2021-06-06