"Это пятый день моего участия в ноябрьском испытании обновлений, ознакомьтесь с подробностями события:Вызов последнего обновления 2021 г.".
Всем привет, меня зовут Чжан Цзиньтао.
В последнее время в сообществе/команде Rust произошли некоторые изменения, так что Rust снова на виду у большинства людей.
Я видел, как многие приятели говорят это в последнее время:
Стоит ли еще изучать Rust? Сообщество нестабильно?
Что лучше Rust или Go?
Стоит ли еще изучать Rust?
Если кто-то задаст мне эти вопросы, мой ответ:
Только дети делают выбор, я хочу их всех!
Конечно, вопросы о Rust и Go тоже не новы, например, предыдущий твит:
В этой статье я расскажу, как вызывать Rust с помощью Go.
Я, конечно, принципиально не буду в этой статье сравнивать функционал Go и Rust, как и производительность этого подхода, просто для интереса.
FFI и привязка
FFI (Foreign Function Interface) переводится как интерфейс внешней функции (далее для простоты будет использоваться FFI). Самая ранняя спецификация от Common Lisp, которая была написана на вики, я не стал исследовать. Однако понятие/термин FFI существует в большинстве языков, которые я использовал, таких как: Python, Ruby, Haskell, Go, Rust, LuaJIT и т. д.
Роль FFI заключается в том, чтобы просто позволить одному языку вызывать другой язык, и иногда мы также используем Binding для выражения подобных возможностей.
Будут разные реализации на разных языках, такие как cgo в Go, ctypes в Python, CAPI в Haskell (раньше был ccall) и т.д. Я лично считаю, что использование FFI в Haskell намного проще и удобнее, чем в других языках, но это не является предметом этой статьи и не будет расширяться.
В этой статье для Go и Rust их FFI должен взаимодействовать с объектами языка C, и эта часть фактически выполняется операционной системой в соответствии с соглашением о вызовах в API.
Давайте перейдем к делу.
Подготовьте пример программы Rust
Установка Rust и базовое использование инструментов Cargo здесь не рассматриваются. Вы можете перейти на официальный сайт Rust, чтобы узнать больше.
Создайте проект с Cargo
Сначала мы подготавливаем каталог для размещения кода этого примера. (Каталог, который я создал, называетсяgo-rust
)
Затем используйте инструмент Rust Cargo, чтобы создатьrustdemo
проект, здесь, так как я добавил--lib
возможность использовать встроенный шаблон библиотеки.
➜ 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. В соответствии с нашими ожиданиями.
Суммировать
В этой статье рассказывается, как использовать Go для объединения с Rust, представлены его предварительные знания о FFI, а затем демонстрируется весь процесс с помощью небольшой практики. Заинтересованные друзья могут практиковаться самостоятельно.
Добро пожаловать, чтобы подписаться на мой публичный аккаунт статьи [MoeLove]