首先沾歪,我們必須安裝 Xcode潘酗,然后設(shè)置 Xcode 構(gòu)建工具贮配。如果您已經(jīng)安裝了構(gòu)建工具并且它們是最新的,則可以跳過此步驟弟疆。
xcode-select --install
xcode-select --install
接下來盒揉,我們需要確保安裝了 Rust,并且我們可以交叉編譯到 iOS 架構(gòu)榆俺。為此售躁,我們將使用rustup。如果你已經(jīng)安裝了 rustup茴晋,你可以跳過這一步陪捷。Rustup 從官方發(fā)布渠道安裝 Rust,使您能夠輕松地在不同的發(fā)布版本之間切換诺擅。它對你未來所有的 Rust 開發(fā)都有用市袖,而不僅僅是在這里。
curl https://sh.rustup.rs -sSf | sh
將 iOS 架構(gòu)添加到 rustup,以便我們可以在交叉編譯期間使用它們苍碟。
這里列出了所有支持的架構(gòu): Rust Cross-compilation Platform Support
rustup target add aarch64-apple-ios armv7-apple-ios armv7s-apple-ios x86_64-apple-ios i386-apple-ios
當(dāng)你安裝 Rust 時酒觅,它也安裝了 cargo,這是一個類似于 pip微峰、gems 等的包管理器∠系ぃ現(xiàn)在我們將使用 cargo 來安裝cargo-lipo。這是一個 cargo 子命令蜓肆,它自動創(chuàng)建一個用于 iOS 的通用庫颜凯。沒有這個箱子,交叉編譯 Rust 以在 iOS 上工作將變得非常困難仗扬。
cargo install cargo-lipo
現(xiàn)在我們都準(zhǔn)備好了症概,可以開始了。讓我們創(chuàng)建項目目錄早芭。
mkdir greetings
cd greetings
cargo new cargo
mkdir iOS
cargo new cargo建立一個全新的 Rust 項目彼城,其默認(rèn)文件和目錄位于名為cargo. 在這個目錄中有一個名為 的文件Cargo.toml,它是包管理器描述符文件退个,還有一個子目錄募壕,src其中包含一個名為lib.rs. 這將包含我們將要執(zhí)行的 Rust 代碼。
我們這里的 Rust 項目是一個超級簡單的 Hello World 庫语盈。它包含一個函數(shù)rust_greeting司抱,該函數(shù)接受一個字符串參數(shù)并返回一個字符串問候該參數(shù)。因此黎烈,如果參數(shù)是“world”,則返回的字符串是“Hello world”匀谣。
打開cargo/src/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)
};
}
讓我們來看看這里發(fā)生了什么。
由于我們將從非 Rust 代碼調(diào)用這個庫武翎,我們實際上將通過 C 橋調(diào)用它烈炭。#[no_mangle]告訴編譯器不要像默認(rèn)情況下那樣破壞函數(shù)名,確保我們的函數(shù)名被導(dǎo)出宝恶,就像它是用 C 語言編寫的一樣符隙。
extern告訴 Rust 編譯器這個函數(shù)將從 Rust 外部調(diào)用,因此確保它是使用 C 調(diào)用約定編譯的垫毙。
接受的字符串rust_greeting是指向 C 字符數(shù)組的指針霹疫。然后我們必須將字符串從 C 字符串轉(zhuǎn)換為 Rust 字符串str。CStr首先我們從指針創(chuàng)建一個對象综芥。然后我們將其轉(zhuǎn)換為 astr并檢查結(jié)果丽蝎。如果發(fā)生錯誤,則沒有提供 arg 并且我們替換there膀藐,否則我們使用提供的字符串的值屠阻。然后我們將提供的字符串附加到我們的問候字符串的末尾以創(chuàng)建我們的返回字符串红省。然后將返回字符串轉(zhuǎn)換為 aCString并傳遞回 C 代碼。
使用CString并返回原始表示將字符串保留在內(nèi)存中并防止它在函數(shù)結(jié)束時被釋放国觉。如果要釋放內(nèi)存吧恃,返回給調(diào)用者的指針現(xiàn)在將指向空內(nèi)存——或者可能完全指向其他東西。但是麻诀,通過確保字符串在函數(shù)執(zhí)行完成后仍然存在痕寓,我們已經(jīng)分配了內(nèi)存并且不再有任何句柄。這是內(nèi)存泄漏的一個秘訣针饥,因此我們必須提供第二個函數(shù)rust_greeting_free厂抽,它接受一個指向 C 字符串的指針并釋放該內(nèi)存。我們必須記住rust_greeting_free從我們的 iOS 代碼調(diào)用以確保我們不會遇到問題丁眼。
我們還需要創(chuàng)建我們的 C 橋筷凤。cargo/src在名為greetings.h. 在該文件中,讓我們定義 C 接口的外觀苞七。我們需要確保我們想要從 iOS 調(diào)用的每個 Rust 函數(shù)都在這里定義藐守。
#include <stdint.h>
const char* rust_greeting(const char* to);
void rust_greeting_free(char *);
讓我們構(gòu)建我們的代碼以確保它有效。為此蹂风,我們必須完成我們的Cargo.toml文件卢厂。這將告訴 cargo 為我們的代碼創(chuàng)建靜態(tài)庫和 C 動態(tài)庫。
[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"]
我們需要使用cargo-lipo. 的構(gòu)建工件將放置在cargo/target/. 我們感興趣的通用 iOS 庫可以在cargo/target/universal/release/libgreetings.a.
cd cargo
cargo lipo --release
這就是我們的 Rust 庫惠啄。讓我們把它與一個 iOS 項目聯(lián)系起來慎恒。
打開 Xcode 并創(chuàng)建一個新項目。轉(zhuǎn)到File\New\Project…并選擇iOS\Application\Single View Application模板撵渡。此模板盡可能接近默認(rèn)的 iOS 應(yīng)用程序融柬。點擊Next。
調(diào)用該項目Greetings趋距,使其成為 Swift 項目粒氧。單擊Next以選擇位置。我們正在使用之前創(chuàng)建的 ios 目錄节腐。如果您選擇另一個位置外盯,您將不得不修改我們稍后設(shè)置的一些路徑。點擊Create翼雀。
從項目導(dǎo)航器中選擇 Greetings 項目饱苟,然后確保選擇了 Greetings 目標(biāo)。打開General選項卡锅纺。向下滾動到該Linked Frameworks and Libraries部分掷空。
通過從 finder 中拖入您的libgreetings.a庫,或單擊列表底部的 +,單擊“添加其他...”并導(dǎo)航至 來導(dǎo)入您的庫cargo/target/universal/release/坦弟。選擇libgreetings.a然后單擊Open护锤。
鏈接libresolv.tbd。單擊 Linked Frameworks 列表底部的 +酿傍,然后在搜索框中鍵入 libresolv烙懦。選擇libresolv.tbd然后“添加”。
iOS 將需要一個橋接標(biāo)頭來訪問我們創(chuàng)建的 C 標(biāo)頭赤炒。首先氯析,讓我們導(dǎo)入greetings.h到我們的 Xcode 項目中,以便我們可以鏈接到它莺褒。轉(zhuǎn)到File\Add files to "Greetings"...導(dǎo)航到greetings.h并選擇Add掩缓。
要創(chuàng)建我們的橋接頭,請轉(zhuǎn)到File\New\File...遵岩。iOS\Source\Header File從提供的選項中選擇并選擇Next你辣。命名文件Greetings-Bridging-Header.h并選擇Create.
打開橋接頭并將其修改為如下所示:
#ifndef Greetings_Bridging_Header_h
#define Greetings_Bridging_Header_h
#import "greetings.h"
#endif
我們需要將橋接標(biāo)頭告知 Xcode。從項目導(dǎo)航器中選擇 Greetings 項目尘执,然后確保選擇了 Greetings 目標(biāo)并打開 Build Settings 選項卡舍哄。將Objective-C Bridging Header選項值設(shè)置為$(PROJECT_DIR)/Greetings/Greetings-Bridging-Header.h
我們還需要告訴 Xcode 在哪里尋找我們的鏈接庫。在同一個 Build Settings 窗格中誊锭,將Library Search Paths選項值修改為$(PROJECT_DIR)/../../cargo/target/universal/release
構(gòu)建你的 xcode 項目表悬,一切都應(yīng)該編譯。
所以丧靡,現(xiàn)在我們已經(jīng)將 Rust 庫導(dǎo)入到我們的 iOS 項目中并成功鏈接到它蟆沫。但是我們?nèi)匀恍枰{(diào)用它。去File\New\File...温治。iOS\Source\Swift File從提供的選項中選擇并選擇Next饥追。命名命名它RustGreetings。
添加以下代碼:
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
}
}
這將創(chuàng)建一個新類罐盔,我們將使用它作為調(diào)用我們的 Rust 庫的接口。這將使我們能夠從應(yīng)用程序的主要代碼中抽象出細(xì)微差別救崔,包括從 C 字符串到 Swift 字符串的轉(zhuǎn)換惶看,并確保我們不會忘記調(diào)用我們的自由函數(shù)并無意中引入內(nèi)存泄漏!
打開ViewController.swift六孵。在函數(shù)內(nèi)部viewDidLoad添加以下代碼:
let rustGreetings = RustGreetings()
print("\(rustGreetings.sayHello(to: "world"))")
現(xiàn)在構(gòu)建您的項目并運行纬黎。模擬器將打開并開始運行您的應(yīng)用程序。當(dāng)視圖加載時劫窒,“Hello world”將在 Xcode 的控制臺窗口中輸出本今。
你可以在Github上找到這個代碼