網站首頁 編程語言 正文
前言
近期 Rust 社區/團隊有些變動,所以再一次將 Rust 拉到大多數人眼前。
我最近看到很多小伙伴說的話:
- Rust 還值得學嗎?社區是不是不穩定呀
- Rust 和 Go 哪個好?
- Rust 還值得學嗎?
這些問題如果有人來問我,那我的回答是:
小孩子才做選擇,我都要!
當然,關于 Rust 和 Go 的問題也不算新,比如之前的一條推文:
我在本篇中就來介紹下如何用 Go 調用 Rust。
當然,這篇中我基本上不會去比較 Go 和 Rust 的功能,或者這種方式的性能之類的,Just for Fun
FFI 和 Binding
FFI (Foreign Function Interface) 翻譯過來叫做外部函數接口(為了比較簡單,下文中都將使用 FFI 指代)。最早來自于 Common Lisp 的規范,這是在 wiki 上寫的,我并沒有去考證。 不過我所使用過的絕大多數語言中都有 FFI 的概念/術語存在,比如:Python、Ruby, Haskell、Go、Rust、LuaJIT 等。
FFI 的作用簡單來說就是允許一種語言去調用另一種語言,有時候我們也會用 Binding 來表示類似的能力。
在不同的語言中會有不同的實現,比如在 Go 中的 cgo , Python 中的 ctypes , Haskell 中的 CAPI (之前還有一個 ccall)等。 我個人感覺 Haskell 中用 FFI 相比其他語言要更簡單&方便的多,不過這不是本篇的重點就不展開了。
在本文中,對于 Go 和 Rust 而言,它們的 FFI 需要與 C 語言對象進行通信,而這部分其實是由操作系統根據 API 中的調用約定來完成的。
我們來進入正題。
準備 Rust 示例程序
Rust 的安裝和 Cargo 工具的基本使用,這里就不介紹了。大家可以去 Rust 的官網進行了解。
用 Cargo 創建項目
我們先準備一個目錄用來放本次示例的代碼。(我創建的目錄叫做 go-rust )
然后使用 Rust 的 Cargo 工具創建一個名叫 rustdemo 的項目,這里由于我增加了 --lib 的選項,使用其內置的 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 暴露出來的函數名叫做 rustdemo ,接收一個外部的參數,并將其打印出來。之后從 Rust 這邊再設置一個字符串。
CString::new(str_name).unwrap().into_raw() 被轉換為原始指針,以便之后由 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 環境下,你如果在其他系統環境下會不同)
? rustdemo git:(master) ? ls target/release/librustdemo.so
target/release/librustdemo.so
準備 Go 代碼
Go 環境的安裝之類的這里也不再贅述了,繼續在我們的 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" 之前的注釋內容是一種特殊的語法,這里是正常的 C 代碼,其中需要聲明使用到的頭文件之類的。
下面的代碼很簡單,定義了一個字符串,傳遞給 rustdemo 函數,然后打印 C 處理后的字符串。
同時,為了能夠讓 Go 程序能正常調用 Rust 函數,這里我們還需要聲明其頭文件,在 lib/rustdemo.h 中寫入如下內容:
char* rustdemo(char *name);
編譯代碼
在 Go 編譯的時候,我們需要開啟 CGO (默認都是開啟的),同時需要鏈接到 Rust 構建出來的 rustdemo.so 文件,所以我們將該文件和它的頭文件放到 lib 目錄下。
? go-rust git:(master) ? cp lib/rustdemo/target/release/librustdemo.so lib
所以完整的目錄結構就是:
? 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 的了。符合我們的預期。
總結
原文鏈接:https://juejin.cn/post/7034707078564282382
相關推薦
- 2022-09-17 C++中String類型的逆序方式_C 語言
- 2023-07-05 全局配置cnpm淘寶鏡像
- 2023-03-22 Redis慢查詢日志及慢查詢分析詳解_Redis
- 2022-12-07 C語言實現堆的簡單操作的示例代碼_C 語言
- 2022-06-16 Go基礎教程系列之defer、panic和recover詳解_Golang
- 2022-03-24 .Net?Core服務治理Consul使用服務發現_自學過程
- 2022-03-16 C++冒泡排序與選擇排序詳解_C 語言
- 2023-02-17 Golang實踐指南之獲取目錄文件列表_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支