Opaque Types
... TODO OVERVIEW ...
Exposing Opaque Rust Types
extern "Rust
sections are used to expose Rust types and their associated methods and functions
so that they can be used from Swift code.
#![allow(unused)] fn main() { mod science; use science::{ScienceLab, Hydrogen, Oxygen, make_water}; #[swift_bridge::bridge] mod ffi { extern "Rust" { type Water; #[swift_bridge(associated_to = Water)] fn new() -> Water; fn is_wet(&self) -> bool; } extern "Rust" { type ScienceLab; type Hydrogen; type Oxygen; fn make_water( lab: &ScienceLab, hydrogen: Hydrogen, oxygen: Oxygen ) -> Water; } } pub struct Water; impl Water { fn new () -> Self { Water } fn is_wet(&self) -> bool { unreachable!("Seriously...?") } } }
Owned, Ref and RefMut
When you define a type in an extern "Rust"
block, three corresponding Swift classes get generated.
// Equivalent to `SomeType` in Rust
class SomeType: SomeTypeRefMut {
// ...
}
// Equivalent to `&mut SomeType` in Rust
class SomeTypeRefMut: SomeTypeRef {
// ...
}
// Equivalent to `&SomeType` in Rust
class SomeTypeRef {
// ...
}
Here's an example of how &Type
and &mut Type
are enforced:
#![allow(unused)] fn main() { // Rust extern "Rust" { type SomeType; #[swift_bridge(init)] fn new() -> SomeType; // Callable by SomeType, SomeTypeRef and SomeTypeRefMut. fn (&self) everyone(); // Callable by SomeType, and SomeTypeRefMut. fn (&mut self) only_owned_and_ref_mut(); // Only callable by SomeType. fn (self) only_owned(); } extern "Rust" { fn make_ref() -> &'static SomeType; fn make_ref_mut() -> &'static mut SomeType; } }
// Swift
func methods() {
let someType: SomeType = SomeType()
let someTypeRef: SomeTypeRef = make_ref()
let someTypeRefMut: SomeTypeRefMut = make_ref_mut()
someType.everyone()
someType.only_owned_and_ref_mut()
someType.only_owned()
someTypeRefMut.everyone()
someTypeRefMut.only_owned_and_ref_mut()
someTypeRef.everyone()
}
func functions() {
let someType: SomeType = SomeType()
let someTypeRef: SomeTypeRef = make_ref()
let someTypeRefMut: SomeTypeRefMut = make_ref_mut()
takeReference(someType)
takeReference(someTypeRef)
takeReference(someTypeRefMut)
}
// Can be called with SomeType, SomeTypeRef and SomeTypeRefMut
func useSomeType(someType: SomeTypeRef) {
// ...
}
Opaque Type Attributes
#[swift_bridge(already_declared)]
The already_declared
attribute allows you to use the same type in multiple bridge modules.
#![allow(unused)] fn main() { use some_crate::App; mod ffi { extern "Rust" { type App; #[swift_bridge(init)] fn new() -> App; } } #[swift_bridge::bridge] #[cfg(feature = "dev-utils")] mod ffi_dev_utils { extern "Rust" { // We won't emit Swift and C type definitions for this type // since we've already declared it elsewhere. #[swift_bridge(already_declared)] type App; fn create_logged_in_user(&mut self, user_id: u8); } } }
#[swift_bridge(Copy($SIZE))]
If you have an opaque Rust type that implements Copy
, you will typically want to be
able to pass it between Swift and Rust by copying the bytes instead of allocating.
For example, let's say you have some new type wrappers for different kinds of IDs within your system.
use uuid:Uuid;
#[derive(Copy)]
struct UserId(Uuid);
#[derive(Copy)]
struct OrganizationId(Uuid);
You can expose them using:
#![allow(unused)] fn main() { #[swift_bridge::bridge] mod ffi { extern "Rust" { #[swift_bridge(Copy(16))] type UserId; #[swift_bridge(Copy(16))] type OrganizationId; } } }
The 16
indicates that a UserId
has 16 bytes.
swift-bridge
will add a compile time assertion that confirms that the given size is correct.
#[swift_bridge(Equatable)]
The Equatable
attribute allows you to expose a Rust PartialEq
implementation via Swift's
Equatable
protocol.
#![allow(unused)] fn main() { #[swift_bridge::bridge] mod ffi { extern "Rust" { #[swift_bridge(Equatable)] type RustPartialEqType; } } #[derive(PartialEq)] struct RustPartialEqType(u32); }
// In Swift
let val1 = RustPartialEqType(5)
let val2 = RustPartialEqType(10)
if val1 == val2 {
print("Equal")
} else {
print("Not equal")
}
#[swift_bridge(Hashable)]
The Hashable
attribute allows you to expose a Rust Hash
trait implementation via Swift's
Hashable
protocol.
#![allow(unused)] fn main() { #[swift_bridge::bridge] mod ffi { extern "Rust" { #[swift_bridge(Hashable)] type RustHashType; } } #[derive(Hash, PartialEq)] struct RustHashType(u32); }
// In Swift
let val = RustHashType(10);
let table: [RustHashType: String] = [:]
table[val] = "hello"
table[val] = "world"
//Should print "world"
print(table[val])