Opaque Types

This chapter explains how to expose opaque handles to Swift classes and Rust structs.

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])

#[swift_bridge(Sendable)]

The Sendable attribute can be added to both opaque Rust and opaque Swift types.

When applied to an opaque Rust type, the generated Swift type will implement Swift's Sendable protocol. swift-bridge will emit code that, at compile time, confirms that the Rust type implements Send + Sync.

When applied to an opaque Swift type, the generated Rust type will implement Rust's Send + Sync traits. swift-bridge will emit code that, at compile time, confirms that the Swift type implements Sendable.


#![allow(unused)]
fn main() {
#[swift_bridge::bridge]
mod ffi {
    extern "Rust" {
        #[swift_bridge(Sendable)]
        type MyRustType;
    }

    extern "Swift" {
        #[swift_bridge(Sendable)]
        type MySwiftType;
    }
}
}

#[swift_bridge(__experimental_ownership)]

The __experimental_ownership attribute instructs swift-bridge to emit code that takes advantage of Swift 6's ownership features.

Once swift-bridge's support for Swift's ownership features stabilizes, this attribute will be removed and the behavior that it enabled will become the default.

When swift-bridge's Swift ownership support is complete, the following will be supported:

  • use Swift's ~Copyable extension to:
    • guarantee at compile time that Swift code cannot use a Rust type that it no longer owns
    • prevent Swift from automatically copying mutable references to Rust types

Note that support for this attribute is a work in progress. Work is tracked in Enforce ownership in generated Swift code https://github.com/chinedufn/swift-bridge/issues/155 .


#![allow(unused)]
fn main() {
#[swift_bridge::bridge]
mod foo {
    extern "Rust" {
        #[swift_bridge(__experimental_swift_ownership)]
        type SomeType;
    }
}
}