Добавить в корзинуПозвонить
Найти в Дзене
Один Rust не п...Rust

Rust & C#

t.me/oneRustnoqRust Для чего нужна данная статья? :
Написать видеосервер с ML Найти компромиссы между разными способами взаимодействия Rust и C#: #[no_mangle] pub extern "C" fn add(a: i32, b: i32) -> i32 { a + b } C#: using System.Runtime.InteropServices;
class Program {
[DllImport("mylib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int add(int a, int b);
static void Main() {
int result = add(2, 3);
Console.WriteLine($"Result: {result}");
}
} #[no_mangle] pub extern "C" fn multiply(a: i32, b: i32) -> i32 { a * b } C++ (промежуточный слой): extern "C" int multiply(int a, int b); C#:
Используйте [DllImport] для вызова C++ обёртки. Пример похож на подход через DLL, но сосредоточен на создании C-совместимого интерфейса. Это полезно, если C# и Rust выполняются в разных процессах. Зачем Вам это уметь? : Обезопасить память используя Rust библиотеки. Использовать Rust для создания безопасного программного обеспечения, включая криптографически
Оглавление
GitHub - nicktretyakov/crust
ML на RUST без заморочек

t.me/oneRustnoqRust

Для чего нужна данная статья? :


Написать видеосервер с ML

Найти компромиссы между разными способами взаимодействия Rust и C#:

1. Через динамическую библиотеку (DLL/SO/DYLIB)

  • Напишите библиотеку на Rust и скомпилируйте её в динамическую библиотеку (.dll на Windows, .so на Linux, .dylib на macOS).
  • В C# используйте атрибут [DllImport] из пространства имен System.Runtime.InteropServices для вызова функций из библиотеки.

#[no_mangle]

pub extern "C" fn add(a: i32, b: i32) -> i32 {

a + b

}

C#:

using System.Runtime.InteropServices;

class Program {
[DllImport("mylib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int add(int a, int b);

static void Main() {
int result = add(2, 3);
Console.WriteLine($"Result: {result}");
}
}

2. Через статическую библиотеку

  • Rust код компилируется в статическую библиотеку (.lib на Windows, .a на Linux/macOS).
  • Затем C# код подключается через промежуточный C/C++ слой с использованием P/Invoke.

#[no_mangle]

pub extern "C" fn multiply(a: i32, b: i32) -> i32 {

a * b

}

C++ (промежуточный слой):

extern "C" int multiply(int a, int b);

C#:
Используйте [DllImport] для вызова C++ обёртки.

3. Через C API

  • Rust библиотека предоставляет интерфейс C API.
  • В C# можно использовать P/Invoke для вызова функций из Rust, которые экспортируются как extern "C".

Пример похож на подход через DLL, но сосредоточен на создании C-совместимого интерфейса.

4. Через cbindgen для автоматической генерации C-заголовков

  • Используйте cbindgen, чтобы автоматически генерировать C-заголовочные файлы из вашего Rust-кода.
  • Эти заголовки упрощают интеграцию Rust в C# через промежуточный слой или P/Invoke.

5. Через FFI с библиотекой UnmanagedExports

  • Вы можете использовать библиотеку UnmanagedExports в C#, чтобы экспортировать функции как интерфейс для взаимодействия с Rust.

6. Через COM (Component Object Model)

  • Напишите Rust-код, который реализует COM-интерфейс.
  • В C# можно подключаться к COM-объекту, используя встроенные средства .NET для работы с COM.

7. Через межпроцессное взаимодействие (IPC)

  • Если прямое взаимодействие не требуется, можно использовать протоколы межпроцессного взаимодействия, такие как:gRPC
    REST API
    (с помощью веб-сервера на Rust, например Actix Web или Axum)
    Unix/Pipe-сокеты

Это полезно, если C# и Rust выполняются в разных процессах.

8. Через .NET Native (с помощью Rust + C# mixed code)

  • Экспериментальный подход с использованием interop библиотек, где вы пишете код для взаимодействия напрямую через платформу Windows (Windows Metadata).

9. Через wasm

  • Скомпилируйте Rust в WebAssembly (WASM) и выполните его в среде, поддерживаемой .NET. Например, с Blazor или другим движком WebAssembly в C#.

10. Через Cxx и CxxSharp

  • Используйте cxx для упрощения интеграции Rust и C++.
  • Затем подключите этот C++ слой в C# с помощью P/Invoke.

Зачем Вам это уметь? :

Обезопасить память используя Rust библиотеки.

Использовать Rust для создания безопасного программного обеспечения, включая криптографические алгоритмы и протоколы.

Написание надежного конкурентного кода.

Использовать библиотеки работающее на разных платформах, включая Windows, macOS, Linux, и веб (через WebAssembly).

Видеосервер C# + Rust + ML

Принимает видеопоток WebRTC
Обрабатывает кадры с
YOLOv8 + OpenCV
Ускорение через
CUDA или wgpu
Поддержка
RabbitMQ для распределённой обработки.

use tokio::sync::mpsc;

use opencv::{prelude::*, videoio, objdetect, core};

use webrtc::peer_connection::RTCPeerConnection;

use std::sync::Arc;

use tch::{CModule, Tensor, Device};

use lapin::{options::*, types::FieldTable, Connection, ConnectionProperties, Channel};

use tonic::transport::Server;

use grpc::video_processing::{video_processor_server::{VideoProcessor, VideoProcessorServer}, ProcessRequest, ProcessResponse};

use std::time::Instant;

#[tokio::main]

async fn main() {

println!("Инициализация системы...");

println!("Используемый GPU: {:?}", Device::cuda_if_available());

let rtc_conn = setup_webrtc().await.unwrap();

let channel = setup_rabbitmq().await.unwrap();

tokio::spawn(start_grpc_server());

println!("Запуск обработки видео...");

process_video(rtc_conn, channel).await;

}

async fn setup_webrtc() -> Result<Arc<RTCPeerConnection>, Box<dyn std::error::Error>> {

let config = webrtc::peer_connection::configuration::RTCConfiguration::default();

let peer_connection = webrtc::api::APIBuilder::new().build().new_peer_connection(config).await?;

Ok(Arc::new(peer_connection))

}

async fn setup_rabbitmq() -> Result<Channel, Box<dyn std::error::Error>> {

let conn = Connection::connect("amqp://localhost:5672", ConnectionProperties::default()).await?;

let channel = conn.create_channel().await?;

Ok(channel)

}

async fn process_video(rtc_conn: Arc<RTCPeerConnection>, channel: Channel) {

let capture = videoio::VideoCapture::new(0, videoio::CAP_ANY).unwrap();

let mut frame = Mat::default();

let model = CModule::load("yolov8.pt").unwrap();

loop {

let start = Instant::now();

capture.read(&mut frame).unwrap();

detect_objects(&frame, &model);

let duration = start.elapsed();

println!("Время обработки кадра: {:?}", duration);

}

}

fn detect_objects(frame: &Mat, model: &CModule) {

let tensor = Tensor::from_data(frame.data_bytes().unwrap()).to_device(Device::cuda_if_available());

let output = model.forward(&[tensor]).unwrap();

println!("Обнаруженные объекты: {:?}", output);

}

async fn start_grpc_server() -> Result<(), Box<dyn std::error::Error>> {

let addr = "[::1]:50051".parse().unwrap();

let video_processor = VideoProcessorService {};

Server::builder().add_service(VideoProcessorServer::new(video_processor)).serve(addr).await?;

Ok(())

}

struct VideoProcessorService;

#[tonic::async_trait]

impl VideoProcessor for VideoProcessorService {

async fn process(&self, request: tonic::Request<ProcessRequest>) -> Result<tonic::Response<ProcessResponse>, tonic::Status> {

println!("Получен gRPC-запрос на обработку видео");

Ok(tonic::Response::new(ProcessResponse { success: true }))

}

}

Здесь setup_webrtc настраивает соединение для видеосвязи, но не блокирует остальной код. Запускает функцию в отдельном потоке, чтобы она работала параллельно с остальным кодом.

  • VideoCapture — захват видео с камеры.
  • Mat — изображение (кадр видео).
  • CModule — загруженная модель нейросети (например, для распознавания объектов).
  • Tensor — данные, которые нейросеть обрабатывает (например, изображение).