Crates.io | objrs |
lib.rs | objrs |
version | 0.0.2 |
source | src |
created_at | 2018-05-14 03:16:57.413514 |
updated_at | 2018-12-02 00:05:51.195529 |
description | Objective-Rust: the unholy union of Rust and Objective-C. |
homepage | |
repository | https://gitlab.com/objrs/objrs |
max_upload_size | |
id | 65272 |
size | 128,340 |
Objective-Rust: the unholy union of Rust and Objective-C.
objrs is a library that makes it easy to write Rust code that interacts with Objective-C code. It's primary purpose is to help Rust applications use Apple's native Objective-C frameworks.
Unless your code is a dumpster fire and you're looking to fuel the flames, do not use objrs in production! The project is in its infancy and is in an extremely experimental phase. The API is not stable, there are many (known and unknown) bugs, undefined behavior is intentionally invoked, etc.
That said, take objrs for a test drive! Play with it. Feedback is very much appreciated, especially if you have any bright ideas on how to better interoperate with Objective-C (e.g. Objective-C protocols map very poorly to Rust and their implementation in objrs is... flawed).
Writing a Rust program that needs to interact with Objective-C code is a painful experience. Either FFI bridges (written in C) need to be created (for every little detail), or you have to use something like objc-rs or rust-objc to do all your interaction with the Objective-C runtime (at runtime! which adds overhead and reduces compiler safety checks).
objrs is different. It provides a collection of macros that apply a series of (questionable) transformations to your code (at compile time!) so that it compiles down to (approximately) the same machine code as pure Objective-C. objrs transforms your pure Rust code so that it is ABI-compatible with Objective-C. Here's an example:
Objective-C:
#import <Foundation/Foundation.h>
int main() {
NSObject *object = [NSObject new];
[object hash];
return 0;
}
This Objective-C program can be implemented in Rust using objrs as follows:
Rust:
extern crate objrs;
use objrs::objrs;
#[objrs(class, root_class)]
#[link(name = "Foundation", kind = "framework")]
struct NSObject;
#[objrs(impl)]
#[link(name = "Foundation", kind = "framework")]
impl NSObject {
// In practice you probably wouldn't want to use #[inline(always)] unless you knew what you were
// doing. Omitting it just results in an extra extern "C" wrapper fn being used, which introduces
// an extra jmp indirection.
#[objrs(selector = "new")]
#[inline(always)]
fn new() -> objrs::Strong<NSObject> {}
#[objrs(selector = "hash")]
#[inline(always)]
fn hash(&self) -> usize {}
}
fn main() {
let object = NSObject::new();
object.hash();
}
Both the Objective-C and Rust code will compile down to the same assembly (modulo some minor costmetic differences, see HOW_IT_WORKS.md for a more detailed breakdown).
Check out the demo
directory for a full demo application (written in 100% Rust).
objrs = "0.0.2"
in your Cargo.toml
's [dependencies]
section.#![feature(rust_2018_preview)]
in your crate's main Rust file to enable nightly features required by objrs.use objrs::objrs
in your code to bring the objrs
macro attribute into scope.objrs also provides framework bindings for Apple's frameworks. The following Apple frameworks have (incomplete) Rust bindings:
objrs_frameworks_app_kit
).objrs_frameworks_core_graphics
).objrs_frameworks_foundation
).objrs_frameworks_metal
).objrs_frameworks_metal_kit
).See the full documentation for more details, including an actual grammar for the attribute macros. Documentation here is intended to be simplistic and introductory.
If you don't plan to implement your own Objective-C bindings or classes, there's no need to depend on the core objrs
crate.
extern crate objrs_frameworks_foundation;
use objrs_frameworks_foundation::NSObject;
fn main() {
// Create a new NSObject instance that is released when it goes out of scope.
let object = NSObject::new();
}
You can create your own class types. Instance variables are supported, but they can only be accessed directly through self
(e.g. self.ivar
will work, some_other_ident.ivar
will not work).
extern crate objrs;
extern crate objrs_frameworks_foundation;
use objrs::objrs;
use objrs_frameworks_foundation::NSObject;
// objrs supports instance variables, even if they have non-trivial constructors and destructors.
#[objrs(class, super = NSObject)]
pub struct StringCollection {
strings: Vec<String>,
}
#[objrs(impl)]
impl StringCollection {
// Using `no_impl` will tell objrs to just let the super class handle the message. Don't populate
// the method body; objrs will do that automatically.
#[objrs(selector = "new", no_impl)]
pub fn new() -> objrs::Strong<StringCollection> {}
// You can still use normal Rust methods. These methods are callable from Rust but not
// Objective-C.
pub fn add_string(&mut self, string: String) {
self.strings.push(string);
}
// This method is callable from Objective-C because it has been given a selector.
#[objrs(selector = "printStrings")]
pub fn print_strings(&self) {
for string in self.strings.iter() {
println!("String: {}", string);
}
}
}
fn main() {
let mut string_collection = StringCollection::new();
string_collection.add_string(String::from("Hello, world!"));
string_collection.print_strings();
}
See the COPYRIGHT file. objrs is triple-licensed under the Apache License 2.0, MIT License, and Mozilla Public License 2.0 terms.