Я хочу и Go, и Rust!

задняя часть Go Rust
Я хочу и Go, и Rust!

"Это пятый день моего участия в ноябрьском испытании обновлений, ознакомьтесь с подробностями события:Вызов последнего обновления 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]