Rust로 .NET 애플리케이션 상호 운용

11227 단어 csharpdotnetrust
외부 기능 인터페이스는 컴퓨터 과학 세계에서 가장 흥미로운 주제 중 하나이며, 한 언어에서 다른 언어로 생성된 결과를 사용할 수 있도록 합니다. 이 게시물에서는 Rust를 .NET Core와 효율적으로 상호 운용하는 방법을 보여드리겠습니다.

왜요?



Rust와 C#은 두 언어 모두에서 매우 강력한 측면을 가지고 있는 반면, C#은 특히 ASP.NET과 효율성 때문에 기업 환경에서 주로 사용됩니다. 두 언어의 힘을 결합하면 올바르게 적용될 때 매우 흥미로운 결과를 얻을 수 있습니다. 그래서 가자.

요구 사항


  • 카고
  • .NET 6

  • 갑시다



    먼저 다음 명령을 사용하여 Rust 프로젝트를 포함할 폴더와 dotnet 프로젝트 및 솔루션을 만듭니다. 저는 dotnet 앱을 "InteropProject"라고 부르고 Rust 코드를 저장할 Native라는 폴더를 만듭니다.

    mkdir InteropProject
    cd InteropProject/
    dotnet new sln
    mkdir InteropProject.Native
    cd InteropProject.Native/
    cargo new --lib my_lib
    cd ..
    dotnet new console -o InteropProject.Console
    dotnet sln add InteropProject.Console
    cd InteropProject.Console/
    mkdir Interop
    


    먼저 Rust 코드에 대해 작업해 보겠습니다. 우리가 원하는 것은 모든 동등한 바인딩과 유형이 포함된 .cs 파일을 생성하는 것입니다. 수동으로 수행하는 것은 지루하고 시간이 많이 걸립니다. 운 좋게도 모든 무거운 작업을 쉽게 수행할 수 있는 라이브러리가 있습니다. 우리를 위해 interoptopus이라고 불렀습니다.

    my_lib 내부에 interoptopus를 추가하고 멀티백엔드 라이브러리이므로 interoptopus_backend_csharp라는 C# 구현이 필요합니다.

    # Cargo.toml
    
    [package]
    name = "my_lib"
    version = "0.1.0"
    edition = "2021"
    publish = false
    
    [lib]
    crate-type = ["cdylib", "rlib"]
    
    [dependencies]
    interoptopus = "0.14.5"
    interoptopus_backend_csharp = "0.14.5" 
    


    interoptopus가 작동하는 방식은 테스트 프로세스에서 생성된 모든 파일을 생성하는 것입니다. mkdir tests로 테스트라는 폴더를 만들고 그 안에 bindings.rs 파일을 추가합니다.

    # tests/bindings.rs
    
    use interoptopus::util::NamespaceMappings;
    use interoptopus::{Error, Interop};
    
    #[test]
    fn bindings_csharp() -> Result<(), Error> {
        use interoptopus_backend_csharp::{Config, Generator};
    
        Generator::new(
            Config {
                class: "InteropBindings".to_string(),
                dll_name: "my_lib".to_string(),
                namespace_mappings: NamespaceMappings::new("InteropProject.Console.Interop"),
                ..Config::default()
            },
            my_lib::my_inventory(),
        )
        .write_file("../../InteropProject.Console/Interop/InteropBindings.cs")?;
    
        Ok(())
    }
    


    처음에는 my_inventory 함수가 없기 때문에 컴파일되지 않습니다. Inventory를 반환하는 C# 코드로 생성되도록 등록된 모든 함수 및 유형을 포함하는 함수입니다. 우리lib.rs를 수정하여 생성해 보겠습니다.

    // src/lib.rs
    
    use interoptopus::{ffi_function, function, Inventory, InventoryBuilder};
    
    #[ffi_function]
    #[no_mangle]
    pub extern "C" fn hello_world() {
        println!("hello world from rust");
    }
    
    pub fn my_inventory() -> Inventory {
        InventoryBuilder::new()
            .register(function!(hello_world))
            .inventory()
    }
    


    여기에서 우리는 C 인터페이스와 extern이 될 hello_wold라는 함수를 생성하고 등록하므로 모든 함수는 extern "C"로 포함되어야 합니다. 이제 Rust 코드를 만들었으니 빌드해 보겠습니다. 실행cargo test && cargo build --target release을 실행하여 라이브러리 바이너리를 생성하고 C# 파일을 생성합니다.

    .csproj 구성



    프로젝트를 실행하려면 C# 애플리케이션 bin 폴더와 동일한 폴더에 모든 바이너리가 있어야 합니다. csproj를 사용하면 바이너리를 항상 복사하도록 dotnet 빌드 명령을 구성할 수 있습니다. 현재 디렉토리를 콘솔 앱으로 변경하고 InteropProject.Console.csproj
    <!-- InteropProject.Console/InteropProject.Console.csproj -->
    
    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
      </PropertyGroup>
    
      <ItemGroup>
         <Content Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'" Include="$(MSBuildProjectDirectory)/../InteropProject.Native/my_lib/target/release/libmy_lib.so">
            <Link>%(Filename)%(Extension)</Link>
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          </Content>
          <Content Condition="'$(OS)' == 'Windows_NT'" Include="$(MSBuildProjectDirectory)/../InteropProject.Native/my_lib/target/release/my_lib.dll">
            <Link>%(Filename)%(Extension)</Link>
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          </Content>
      </ItemGroup>
    
    </Project>
    
    


    이제 dotnet build를 실행할 때마다 Linux에 있는 경우 .so가 추가되고 Windows에 있는 경우 .dll가 구성을 생성합니다.

    이제 or Program.cs 에서 함수를 호출해 보겠습니다.

    // InteropProject.Console/Program.cs
    
    using InteropProject.Console.Interop;
    
    InteropBindings.hello_world();
    

    dotnet run를 실행하면 다음 메시지가 표시됩니다.

    hello world from rust
    


    축하합니다



    이제 .NET 애플리케이션을 Rust와 상호 운용할 수 있습니다. 다음을 사용하여 빌드 및 실행 프로세스를 자동화하는 셸 스크립트 또는 powershell 스크립트를 만들 수도 있습니다.

    #!/usr/bin/env bash
    
    PROJECT_DIR="$(pwd)"
    
    function main() {
        cd "$(PROJECT_DIR)/InteropProject.Native/my_lib"
        cargo test && cargo build --target release
        cd "$(PROJECT_DIR)/InteropProject.Console/"
        dotnet build
    }
    
    main
    


    시간 내 주셔서 감사합니다



    이 튜토리얼이 마음에 든다면 공유하고 좋아요를 눌러주세요! 의심스러운 점이 있으면 최선을 다해 도와드리겠습니다.

    좋은 웹페이지 즐겨찾기