首先,我們必須安裝 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 時(shí),它也安裝了 cargo,這是一個(gè)類似于 pip、gems 等的包管理器?,F(xiàn)在我們將使用 cargo 來安裝cargo-lipo。這是一個(gè) cargo 子命令,它自動(dòng)創(chuàng)建一個(gè)用于 iOS 的通用庫。沒有這個(gè)箱子,交叉編譯 Rust 以在 iOS 上工作將變得非常困難。
cargo install cargo-lipo
現(xiàn)在我們都準(zhǔn)備好了,可以開始了。讓我們創(chuàng)建項(xiàng)目目錄。
mkdir greetings
cd greetings
cargo new cargo
mkdir iOS
cargo new cargo建立一個(gè)全新的 Rust 項(xiàng)目,其默認(rèn)文件和目錄位于名為cargo. 在這個(gè)目錄中有一個(gè)名為 的文件Cargo.toml,它是包管理器描述符文件,還有一個(gè)子目錄,src其中包含一個(gè)名為lib.rs. 這將包含我們將要執(zhí)行的 Rust 代碼。
我們這里的 Rust 項(xiàng)目是一個(gè)超級(jí)簡單的 Hello World 庫。它包含一個(gè)函數(shù)rust_greeting,該函數(shù)接受一個(gè)字符串參數(shù)并返回一個(gè)字符串問候該參數(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)用這個(gè)庫,我們實(shí)際上將通過 C 橋調(diào)用它。#[no_mangle]告訴編譯器不要像默認(rèn)情況下那樣破壞函數(shù)名,確保我們的函數(shù)名被導(dǎo)出,就像它是用 C 語言編寫的一樣。
extern告訴 Rust 編譯器這個(gè)函數(shù)將從 Rust 外部調(diào)用,因此確保它是使用 C 調(diào)用約定編譯的。
接受的字符串rust_greeting是指向 C 字符數(shù)組的指針。然后我們必須將字符串從 C 字符串轉(zhuǎn)換為 Rust 字符串str。CStr首先我們從指針創(chuàng)建一個(gè)對象。然后我們將其轉(zhuǎn)換為 astr并檢查結(jié)果。如果發(fā)生錯(cuò)誤,則沒有提供 arg 并且我們替換there,否則我們使用提供的字符串的值。然后我們將提供的字符串附加到我們的問候字符串的末尾以創(chuàng)建我們的返回字符串。然后將返回字符串轉(zhuǎn)換為 aCString并傳遞回 C 代碼。
使用CString并返回原始表示將字符串保留在內(nèi)存中并防止它在函數(shù)結(jié)束時(shí)被釋放。如果要釋放內(nèi)存,返回給調(diào)用者的指針現(xiàn)在將指向空內(nèi)存——或者可能完全指向其他東西。但是,通過確保字符串在函數(shù)執(zhí)行完成后仍然存在,我們已經(jīng)分配了內(nèi)存并且不再有任何句柄。這是內(nèi)存泄漏的一個(gè)秘訣,因此我們必須提供第二個(gè)函數(shù)rust_greeting_free,它接受一個(gè)指向 C 字符串的指針并釋放該內(nèi)存。我們必須記住rust_greeting_free從我們的 iOS 代碼調(diào)用以確保我們不會(huì)遇到問題。
我們還需要?jiǎng)?chuàng)建我們的 C 橋。cargo/src在名為greetings.h. 在該文件中,讓我們定義 C 接口的外觀。我們需要確保我們想要從 iOS 調(diào)用的每個(gè) 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 動(dòng)態(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 庫。讓我們把它與一個(gè) iOS 項(xiàng)目聯(lián)系起來。
打開 Xcode 并創(chuàng)建一個(gè)新項(xiàng)目。轉(zhuǎn)到File\New\Project…并選擇iOS\Application\Single View Application模板。此模板盡可能接近默認(rèn)的 iOS 應(yīng)用程序。點(diǎn)擊Next。

調(diào)用該項(xiàng)目Greetings,使其成為 Swift 項(xiàng)目。單擊Next以選擇位置。我們正在使用之前創(chuàng)建的 ios 目錄。如果您選擇另一個(gè)位置,您將不得不修改我們稍后設(shè)置的一些路徑。點(diǎn)擊Create。

從項(xiàng)目導(dǎo)航器中選擇 Greetings 項(xiàng)目,然后確保選擇了 Greetings 目標(biāo)。打開General選項(xiàng)卡。向下滾動(dòng)到該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 將需要一個(gè)橋接標(biāo)頭來訪問我們創(chuàng)建的 C 標(biāo)頭。首先,讓我們導(dǎo)入greetings.h到我們的 Xcode 項(xiàng)目中,以便我們可以鏈接到它。轉(zhuǎn)到File\Add files to "Greetings"...導(dǎo)航到greetings.h并選擇Add。
要?jiǎng)?chuàng)建我們的橋接頭,請轉(zhuǎn)到File\New\File...。iOS\Source\Header File從提供的選項(xiàng)中選擇并選擇Next。命名文件Greetings-Bridging-Header.h并選擇Create.

打開橋接頭并將其修改為如下所示:
#ifndef Greetings_Bridging_Header_h
#define Greetings_Bridging_Header_h
#import "greetings.h"
#endif
我們需要將橋接標(biāo)頭告知 Xcode。從項(xiàng)目導(dǎo)航器中選擇 Greetings 項(xiàng)目,然后確保選擇了 Greetings 目標(biāo)并打開 Build Settings 選項(xiàng)卡。將Objective-C Bridging Header選項(xiàng)值設(shè)置為$(PROJECT_DIR)/Greetings/Greetings-Bridging-Header.h

我們還需要告訴 Xcode 在哪里尋找我們的鏈接庫。在同一個(gè) Build Settings 窗格中,將Library Search Paths選項(xiàng)值修改為$(PROJECT_DIR)/../../cargo/target/universal/release

構(gòu)建你的 xcode 項(xiàng)目,一切都應(yīng)該編譯。
所以,現(xiàn)在我們已經(jīng)將 Rust 庫導(dǎo)入到我們的 iOS 項(xiàng)目中并成功鏈接到它。但是我們?nèi)匀恍枰{(diào)用它。去File\New\File...。iOS\Source\Swift File從提供的選項(xiàng)中選擇并選擇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)建一個(gè)新類,我們將使用它作為調(diào)用我們的 Rust 庫的接口。這將使我們能夠從應(yīng)用程序的主要代碼中抽象出細(xì)微差別,包括從 C 字符串到 Swift 字符串的轉(zhuǎn)換,并確保我們不會(huì)忘記調(diào)用我們的自由函數(shù)并無意中引入內(nèi)存泄漏!
打開ViewController.swift。在函數(shù)內(nèi)部viewDidLoad添加以下代碼:
let rustGreetings = RustGreetings()
print("\(rustGreetings.sayHello(to: "world"))")
現(xiàn)在構(gòu)建您的項(xiàng)目并運(yùn)行。模擬器將打開并開始運(yùn)行您的應(yīng)用程序。當(dāng)視圖加載時(shí),“Hello world”將在 Xcode 的控制臺(tái)窗口中輸出。
你可以在Github上找到這個(gè)代碼