在 iOS 上構(gòu)建和部署 Rust 庫

首先,我們必須安裝 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。

image.png

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

image.png

從項(xiàng)目導(dǎo)航器中選擇 Greetings 項(xiàng)目,然后確保選擇了 Greetings 目標(biāo)。打開General選項(xiàng)卡。向下滾動(dòng)到該Linked Frameworks and Libraries部分。

image.png

通過從 finder 中拖入您的libgreetings.a庫,或單擊列表底部的 +,單擊“添加其他...”并導(dǎo)航至 來導(dǎo)入您的庫cargo/target/universal/release/。選擇libgreetings.a然后單擊Open。

鏈接libresolv.tbd。單擊 Linked Frameworks 列表底部的 +,然后在搜索框中鍵入 libresolv。選擇libresolv.tbd然后“添加”。

image.png

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.

image.png

打開橋接頭并將其修改為如下所示:

#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

image.png

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

image.png

構(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。

image.png

添加以下代碼:

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è)代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容