ios開発にRustを使う 環境構築編
iOSでは、Objective-C(C++も書ける)、Swift しか使えません
が、C++の 異言語間インタフェース FFIを使えば、Objectibe-CやSwiftから C++を通して Rustを実行出来ます
そのための環境構築
とりあえず xcodeでのビルドを行う
Rustのインストール
Rust自体は パッケージマネージャから出来るが、その場合だと Rustのバージョン管理などが不便なので
もしインストールしている場合は 一旦削除し、Rustupをインストールする
環境構築は下記参照
Building and Deploying a Rust library on iOS
書いてる通り実行すれば出来るけど一応簡単に
xcode build toolのインストール
xcode-select --install
にて、xcodeのインストール
rustupのインストール
curl https://sh.rustup.rs -sSf | sh
rustupをインストールする
ios、iosシミュレータ用のToolChainのインストール
rustup target add aarch64-apple-ios armv7-apple-ios armv7s-apple-ios x86_64-apple-ios i386-apple-ios
Cargoのインストール
cargo install cargo-lipo
パッケージマネージャ&ビルドツールのCargoは是非入れておくべきだ
環境変数
bash_profile 等に cargoのパスの設定が入るので、sourceして環境変数を読み込み直す
source ~/.bash_profile
サンプル
これまた、上記URLのとおりにすればよい
Rustファイル作成
lib.rs
use std::os::raw::{c_char}; use std::ffi::{CString, CStr}; #[no_mangle] pub extern fn rust_greeting(to: *const c_char) -> *mut c_char { let c_str = unsafe { CStr::from_ptr(to) }; let recipient = match c_str.to_str() { Err(_) => "there", Ok(string) => string, }; CString::new("Hello ".to_owned() + recipient).unwrap().into_raw() } #[no_mangle] pub extern fn rust_greeting_free(s: *mut c_char) { unsafe { if s.is_null() { return } CString::from_raw(s) }; }
Rustの文法は今回は省略
rust_greeting関数は 引数に文字列を受け取り "Hello " を頭につけて返す
rust_greeting_freeは 作成した文字列を開放する
FFI用ヘッダ
greetings.h
#include <stdint.h> const char* rust_greeting(const char* to); void rust_greeting_free(char *);
上記のRust関数を呼び出すためのヘッダである
toml
Rustのビルドのしかたを教えなければならない
Cargo.toml
[package] name = "greetings" version = "0.1.1" authors = ["fluffyemily <fluffyemily@mozilla.com>"] description = "Example static library project built for iOS" publish = false [lib] name = "greetings" crate-type = ["staticlib", "cdylib"]
今回は、ライブラリを作成するので、create-typeは スタティックライブラリとダイナミックライブラリを指定した
rustのビルド
cargo lipo --release
lipoを使うと便利である
今はToolChainに iOS関連をいれているので、それらがビルドされる
また、Universalライブラリという、全てのToolChainのライブラリがまとまったものが作成されるので
それを指定すれば、エミュレータでも実機でも、32bitでも64bitでも 設定変更不要で便利だ
xcodeでプロジェクト作成
とりあえずSinglePageプロジェクトを作成し
先程の作成した Universalライブラリと、libresolvをリンクする
また、リンクパスに Universalの場所を指定する
Swift Bridgeファイル
RustからC++へのブリッジは 上記 ライブラリ作成でC++とアクセス可能だ
Objective-CはC++と互換性があり そのまま実行できる
SwiftとC++を相互呼び出しするために Bridgeを作る
#ifndef Greetings_Bridging_Header_h #define Greetings_Bridging_Header_h #import "greetings.h" #endif
この、greetings.hは 先程作った Rust関数のC言語ヘッダである
importで怒られると思うが、ヘッダのSearchPathを追加すれば良い
Swiftコード作成
RustGreetings.swift
class RustGreetings { func sayHello(to: String) -> String { let result = rust_greeting(to) let swift_result = String(cString: result!) rust_greeting_free(UnsafeMutablePointer(mutating: result)) return swift_result } }
RustのBridgeをした関数 rust_greeting、rust_greeting_free を呼び出すWrapクラス
多少面倒だが、こうやって呼び出すことが出来る
これを実行するために ViewControllerの viewDidLoadedに 上記を呼び出すコードを書く
let rustGreetings = RustGreetings() print("\(rustGreetings.sayHello(to: "world"))")
実行
実行すると Hello World とコンソールに出るはずだ