На Хабре в свое время была статья о том, как вызвать Rust код из Go. Статья неплохая, но довольно сложная для понимания и на самом деле отталкивающая новичков от желания смотреть в сторону обоих языков. Цель этого поста не столько залезть в «кишки» кросс-языковых вызовов, сколько показать насколько просто это можно сделать.
Далеко ходить не будем и возмем пример из книги по изучению языка Rust.
Все, что делает этот пример, это запускает 10 потоков, внутри которых инкрементирует переменую 'x' до 5 миллионов и выводит сообщение о завершении потока.
Также нужно отредактировать cargo-файл добавив в него строку
В целом это все, что вы должны сделать в Rust-библиотеке. Пример из книги описывает это более детально и останаливаться на этом мы не будем.
Компилируем библиотеку командой:
Внутри Go-файл добаляем вот такие директивы и наш main.go будет выглядеть вот так
Собираем проект командой
Все. Запускаем программу и получаем вывод.
Также отдельно стоит сказать, что можно передать зачения из Go-программы в Rust-библиотеку. Для этого преобразуем функцию в Rust-библиотеке, чтобы она принимала строковое значение.
Собираем наш проект, снова копируем библиотеку в папку lib, модифицируем файл process.h вот таким образом
Передаем строку из Go-приложения (в нашем случае: «Hello from Golang»).
Все. Запускаем программу и получаем вывод.
Далеко ходить не будем и возмем пример из книги по изучению языка Rust.
Все, что делает этот пример, это запускает 10 потоков, внутри которых инкрементирует переменую 'x' до 5 миллионов и выводит сообщение о завершении потока.
use std::thread;
#[no_mangle]
pub extern "C" fn process() {
let handles:Vec<_> = (0..10).map(|_|{
thread::spawn(||{
let mut x = 0;
for _ in 0..5_000_000 {
x += 1
}
x
})
}).collect();
for h in handles {
println!("Thread finished with count={}",
h.join().map_err(|_| "Could not join thread!").unwrap());
}
println!("Done!");
}
Также нужно отредактировать cargo-файл добавив в него строку
crate-type = [«cdylib»], в результате чего будет создана библиотека с возможностью вызова фукции через Foreign Function Interface (FFI).
Стоит заметить, что libembed.dylib — это библиотека на Mac OS, на Linux будет libembed.so, а на Windows — libembed.dll
Спасибо: bingo347
Cargo.toml
[package]
name = "embed"
version = "0.1.0"
[lib]
crate-type = ["cdylib"]
В целом это все, что вы должны сделать в Rust-библиотеке. Пример из книги описывает это более детально и останаливаться на этом мы не будем.
Компилируем библиотеку командой:
cargo build --releaseТеперь наша цель вызвать этот код из Go-приложение. Создаем простое приложение и внутри нашего проекта, добавляем папку lib, в которую копируем файл /target/release/libembed.dylib. Внутри создаем файл с названием функции и описываем его сигнатуру вызова.
lib/process.h
void process();
Внутри Go-файл добаляем вот такие директивы и наш main.go будет выглядеть вот так
package main
/*
#cgo LDFLAGS: -L./lib -lembed
#include "./lib/process.h"
*/
import "C"
func main() {
C.process()
}
Собираем проект командой
go build -ldflags="-r /lib" main.goОбратите внимание на параметр ldflags, в данном случае все, что мы делаем это устанавливаем путь к ELF dynamic linker.
Все. Запускаем программу и получаем вывод.
Также отдельно стоит сказать, что можно передать зачения из Go-программы в Rust-библиотеку. Для этого преобразуем функцию в Rust-библиотеке, чтобы она принимала строковое значение.
extern crate libc;
use std::thread;
use std::ffi::CStr;
#[no_mangle]
pub extern "C" fn process(name: *const libc::c_char) {
let buf_name = unsafe { CStr::from_ptr(name).to_bytes() };
let str_name = String::from_utf8(buf_name.to_vec()).unwrap();
let handles:Vec<_> = (0..10).map(|_|{
thread::spawn(||{
let mut x = 0;
for _ in 0..5_000_000 {
x += 1
}
x
})
}).collect();
for h in handles {
println!("{}:Thread finished with count={}\n",
str_name,
h.join().map_err(|_| "Could not join thread!\n").unwrap());
}
println!("Done!");
}
Собираем наш проект, снова копируем библиотеку в папку lib, модифицируем файл process.h вот таким образом
void process(char *name);
Передаем строку из Go-приложения (в нашем случае: «Hello from Golang»).
package main
/*
#cgo LDFLAGS: -L./lib -lembed
#include "./lib/process.h"
*/
import "C"
func main() {
C.process(C.CString("Hello from Golang !!"))
}
Все. Запускаем программу и получаем вывод.