Panggil fungsi Rust dari Go

Pada suatu waktu, ada sebuah artikel tentang Habr tentang cara memanggil kode Rust dari Go . Artikel ini tidak buruk, tetapi agak sulit untuk dipahami dan benar-benar mengusir pemula dari keinginan untuk melihat ke arah kedua bahasa. Tujuan dari posting ini bukan untuk mendapatkan keberanian dari panggilan lintas-bahasa, tetapi untuk menunjukkan betapa mudahnya hal itu dapat dilakukan.

gambar

Kami tidak akan melangkah jauh dan mengambil contoh dari sebuah buku tentang belajar bahasa Rust.

Semua yang dilakukan contoh ini adalah memulai 10 utas, di mana ia menambahkan variabel 'x' menjadi 5 juta dan menampilkan pesan tentang akhir aliran.

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!");
}


Anda juga perlu mengedit file kargo dengan menambahkan baris
crate-type = ["cdylib"]
Akibatnya, perpustakaan akan dibuat dengan kemampuan untuk memanggil fungsi melalui Antarmuka Fungsi Asing (FFI) .

Perlu dicatat bahwa libembed.dylib adalah perpustakaan di Mac OS, di Linux itu akan libembed.so, dan pada Windows itu akan libembed.dll

Terima kasih: bingo347


Cargo.toml
[package]
name = "embed"
version = "0.1.0"

[lib]
crate-type = ["cdylib"]


Secara umum, ini semua harus Anda lakukan di perpustakaan Rust. Contoh dari buku ini menjelaskan hal ini secara lebih rinci dan kami tidak akan membahasnya.

Kami mengkompilasi perpustakaan dengan perintah:
build kargo --release
Sekarang tujuan kami adalah untuk memanggil kode ini dari aplikasi Go. Kami membuat aplikasi sederhana dan di dalam proyek kami, tambahkan folder lib ke mana kita menyalin file /target/release/libembed.dylib . Di dalam, kami membuat file dengan nama fungsi dan menjelaskan tanda tangan panggilannya.

lib / process.h
void process();


Di dalam Go-file kami menambahkan arahan tersebut dan main.go kami akan terlihat seperti ini

package main

/*
#cgo LDFLAGS: -L./lib -lembed
#include "./lib/process.h"
*/
import "C"

func main() {
	C.process()
}


Menyatukan proyek
buka build -ldflags = "- r / lib" main.go
Perhatikan parameter ldflags, dalam hal ini yang kita lakukan hanyalah mengatur path ke ELF dynamic linker.

Semua. Jalankan program dan dapatkan hasilnya.



Perlu juga disebutkan secara terpisah bahwa Anda dapat mentransfer pengajaran dari Go-program ke Rust-library. Untuk melakukan ini, kami mengubah fungsi di perpustakaan Rust sehingga dibutuhkan nilai string.

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!");
}


Kami merakit proyek kami, menyalin perpustakaan ke folder lib lagi, memodifikasi file process.h dengan cara ini
proses batal (char * name);

Kami melewati garis dari aplikasi-Go (dalam kasus kami: "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 !!"))
}


Semua. Jalankan program dan dapatkan hasilnya.


All Articles