John Gallagher - Big Nerd Ranch Tue, 19 Oct 2021 17:46:22 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 Building an iOS App in Rust, Part 4: A Basic View Model in Rust https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-4-a-basic-view-model-in-rust/ https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-4-a-basic-view-model-in-rust/#respond Wed, 13 Apr 2016 09:53:37 +0000 https://nerdranchighq.wpengine.com/blog/building-an-ios-app-in-rust-part-4-a-basic-view-model-in-rust/ Welcome to Part 4 of the "Let's Build an iOS App in Rust" series. We'll continue to build on the tools and techniques we explored in the first three parts, creating a basic view model in Rust.

The post Building an iOS App in Rust, Part 4: A Basic View Model in Rust appeared first on Big Nerd Ranch.

]]>

Editor’s note: This is the fourth post in our series on building an iOS app in Rust.

Welcome to Part 4 of the “Let’s Build an iOS App in Rust” series. We’ll continue to build on the tools and techniques we explored in the first three parts:

  • In Part 1, we set up a Rust toolchain for cross-compiling to iOS.
  • In Part 2, we covered how to pass primitive data from Swift to Rust and vice versa across Rust’s foreign function interface (FFI).
  • In Part 3, we moved on to cross-language memory management, passing “owned” objects that need to be deallocated between Swift and Rust.

In this post, we’re going to create a basic view model in Rust. This will be our first foray into multithreading, because the view model needs to be accessible from Swift on the main thread (we’ll use it to implement a UITableViewDataSource), but we’re going to send updates from background threads managed entirely by Rust.

As always, the code for this post is available on GitHub. I will not go over every line of code in this post, but you can download and run the sample project from there. It may also be helpful to look at some of the code I do point out in context.

Options for Sharing between Swift and Rust

The goal for this post is a view model, written in Rust, that is:

  • accessible from Swift on the iOS main thread, and
  • updatable from Rust on background threads.

This sets us up perfectly to introduce data races, a particularly insidious kind of race condition. A data race occurs when two or more threads have simulatenous read or write access to a memory location and at least one of the threads is trying to write to that location. Data races introduce undefined behavior—maybe your app crashes, maybe it works correctly, maybe it continues to run—but does the wrong thing at runtime. Bad news.

In a pure Rust program (assuming no unsafe code), data races do not exist, because Rust’s ownership system will enforce rules that prevent them at compile time. Because we’re going to be working across two different languages, we’re not going to be able to statically guarantee correctness like we could in a pure Rust program. We need a strategy to help make sure we do the right thing. I’ll propose two different strategies for managing this complexity.

The first strategy is to avoid the data race by giving Swift its own copy of
the view model every time we make a change from Rust. This will avoid the data
race by avoiding sharing altogether: whenever a Rust background thread updates
the view model, it will take a snapshot of the new view model state and give
that back to Swift. Swift will be responsible for destroying all the view model
copies it gets, but we won’t have to worry about concurrent access, since Swift
owns (and holds the only reference to) its copies. Dropbox uses this technique in their
cross-platform C++ libraries.

I don’t know about you, but my initial reaction to that strategy was one of…
incredulity. Isn’t in incredibly expensive to create a new snapshot of the view
model state every time you change it? As is always the case with performance
concerns, the answer is “it depends.” If your view model state is “small” (for
some definition of small), it’s probably fine. If your view model state is big
but you can figure out a way to make its in-memory representation small, it’s
probably fine then, too. (An example of this would be if your view model is
holding an array of images—you don’t need to copy all the image data every
time; you can just make more references to the same unchanging data.)

The second strategy we’ll examine is to truly share a single instance of a view
model between the two languages by giving Rust a way to perform updates on the
iOS main thread. This will be make use of some relatively advanced techniques
on the Rust side, so we’ll explore this strategy in the next post in this series.

“Copying” the View Model

Let’s look at the view model implementation on the Rust side. Here is a
subset of the code (you can see the full source on
GitHub
):

#[derive(Clone)]
pub struct ViewModel {
    values: Vec<String>,
}

impl ViewModel {
    pub fn len(&self) -> usize {
        self.values.len()
    }

    pub fn value_at_index(&self, index: usize) -> &str {
        &self.values[index]
    }
}

Our view model is very simple: we have a vector of Strings that we’ll
display in a table view on the iOS side. We’ll have a number of Rust threads
that modify values in various ways (appending/deleting/modifying
existing).

There are a couple of things to note about this Rust code:

  • The ViewModel struct derives an implementation for the Clone trait. This means we can call the .clone() method on a ViewModel instance, and we’ll get back a copy that is made by calling .clone() on each of ViewModel’s fields (the values vector, in this case).
  • The value_at_index method returns a reference to memory inside the view model itself. This is fine because Swift is going to be the sole owner of ViewModel instances. The &str references returned by value_at_index will continue to be valid until the ViewModel is destroyed.

The View Model Observer

Ok, so if this is the view model we’ll give to Swift, what does the “giving”?
We need another type, a ViewModelHandle, and a Rust trait (very similar to a
Swift protocol) defining how we’ll provide update notifications back to iOS:

pub trait ViewModelObserver: Send + 'static {
    fn inserted_item(&self, view_model: ViewModel, index: usize);
    fn removed_item(&self, view_model: ViewModel, index: usize);
    fn modified_item(&self, view_model: ViewModel, index: usize);
}

// TODO: What should we store in the ViewModelHandle?
pub struct ViewModelHandle;

impl ViewModelHandle {
    pub fn new<Observer>(num_threads: usize, observer: Observer) -> (ViewModelHandle, ViewModel)
        where Observer: ViewModelObserver
    {
        // TODO: Create a ViewModel and a ViewModelHandle.
        // Start up `num_threads` threads that will modify the ViewModel
        // and notify `observer` when they do so.
    }
}

First let’s look at ViewModelObserver. It’s a trait that requires
implementors to provide three methods: we’ll call inserted_item whenever
we update the view model by appending a new value, removed_item whenever
we update the view model by removing a value, and modified_item whenever
we update the view model by changing an existing value. In all three cases,
we’ll pass back to Swift a copy of the the current ViewModel (after the
change we made) and the index of the value that was added/removed/changed.

One curious thing about the ViewModelObserver is that it requires its
implementors to also be Send + 'static. Send is a special trait: it
doesn’t have any method requirements; instead, it’s used by the Rust compiler
to indicate that a type can be transferred from one thread to another. We need
this requirement because we’re going to be calling methods on the observer from
Rust background threads. 'static is a requirement on the lifetime of the
type conforming to ViewModelObserver: it cannot have references to other
Rust objects that could possibly be deallocated while the thread is running
(i.e., non-'static references).

Before we can figure out how to flesh out ViewModelHandle, let’s think about
what we’re going to do on the Rust background threads. Every thread needs a
reference to the same view model and observer (so it can make changes and notify
the observer). Since all threads want write access to the same memory, we must
protect that memory somehow. Let’s define a new struct, Inner, that contains
the memory we want to share:

struct Inner<Observer: ViewModelObserver> {
    view_model: ViewModel,
    observer: Observer,
}

To create a safe, shared reference to a single instance of Inner, we need two
types from the Rust standard library:

  • Arc<T> is an “atomically reference counted” T. Arc provides a way to wrap up an instance of a type to be shared across multiple threads. If you call .clone() on an Arc<T>, you increase the reference count but don’t actually clone the contained instance. The T that is contained in the Arc will be deallocated once all the references to it go out of scope.
  • Mutex<T> is a mutually exclusive lock protecting a value of type T. In Rust, a mutex isn’t standalone; it is always protecting something. In order to read or write the protected T, one must first call .lock() on the mutex to get back a MutexGuard; the mutex will unlock when the MutexGuard goes out of scope.

(Note: Rust’s Arc type is completely unrelated to Swift and Objective-C’s
ARC system. They happen to share the same short name, but Rust’s “atomically
reference counted” type is a part of its standard library, while Swift’s
“automatic reference counting” is part of the compiler.)

The Background Thread Worker

We’ll combine these and create an Arc<Mutex<Inner>>; i.e., an atomically
referenced counted mutex containing an Inner. We’ll give all our background
thread workers a clone of this value (i.e., they all get references to the
same Mutex), an ID number they can use when updating the view model strings,
and a way to determine when they should stop executing. Let’s combine these
into a ThreadWorker struct:

struct ThreadWorker<Observer: ViewModelObserver> {
    inner: Arc<Mutex<Inner<Observer>>>,
    thread_id: usize,
    shutdown: mpsc::Receiver<()>,
}

What is an mpsc::Receiver<()>? It’s the receiving end of a channel
that lets us communicate safely between threads. In this case, we’re not actually
going to send any data at all; instead, we’ll use this to implement a
should_shutdown method on ThreadWorker that will return true if the
sending end of the channel has been disconnected (i.e., because it’s been
deallocated):

impl<Observer: ViewModelObserver> ThreadWorker<Observer> {
    fn should_shutdown(&self) -> bool {
        match self.shutdown.try_recv() {
            // Disconnected means the sending end is gone - we should stop.
            Err(mpsc::TryRecvError::Disconnected) => true,

            // Empty means the sending end is still alive - we should keep running.
            Err(mpsc::TryRecvError::Empty) => false,

            // We should never actually receive data - panic if we do.
            Ok(()) => unreachable!("thread worker channels should not be used directly"),
        }
    }
}

The three things we want to do from a ThreadWorker are to add a value, remove
a valu, or modify a value. The implementations for all three are similar, so
we’ll look at adding a value here and you can see the other two on
GitHub
:

impl<Observer: ViewModelObserver> ThreadWorker<Observer> {
    fn add_new_item(&self) {
        // Lock the mutex. (unwrap() asserts that the mutex isn't "poisoned", which
        // occurs if another thread panics while holding the mutex.)
        let mut inner = self.inner.lock().unwrap();

        // With the mutex locked, push a string containing our thread_id onto
        // the view model.
        inner.view_model.push(format!("rust-thread-{}", self.thread_id));

        // Still with the mutex locked, notify the observer that we inserted an item,
        // giving them a clone of the current view model state and the index of the
        // item we added.
        inner.observer.inserted_item(inner.view_model.clone(), inner.view_model.len() - 1);
    }
}

When we lock a mutex, we get back a MutexGuard. This is a
powerful type: it gives us write access to the contained value (an Inner, in
our case), and it automatically unlocks the mutex when the guard falls out of
scope (when add_new_item returns). We’ll discuss why we continue to hold the
mutex locked while we call back to the observer toward the end of this post.

We can now implement the “main” function for our thread workers (some non-essential
details are omitted):

impl<Observer: ViewModelObserver> ThreadWorker<Observer> {
    fn main(&self) {
        // add a new item immediately
        self.add_new_item();

        // loop "forever"
        loop {
            // sleep for some random-ish amount of time (1-4 seconds)
            thread::sleep(...);

            // check and see if we should stop
            if self.should_shutdown() {
                println!("thread {} exiting", self.thread_id);
                return;
            }

            // 20% of the time, add a new item.
            // 10% of the time, remove an item.
            // 70% of the time, modify an existing item.
            match random_number_between_1_and_10() {
                0 | 1 => self.add_new_item(),
                2 => self.remove_existing_item(),
                _ => self.modify_existing_item(),
            }
        }
    }
}

So every thread worker we start up with first add an item, then go into an
infinite loop where they sleep, shutdown if they should, and then modify the
view model randomly in some way.

Let’s go back and fill in ViewModelHandle. We know our handle will need to
hold on to the sending side of one channel for each background thread. It turns
out that’s the only thing it needs to hold on to: we can create the shared
view model and give it to all the background threads, after which the handle
itself never needs it.

// ViewModelHandle holds a vector of senders corresponding to each ThreadWorker's receiver.
pub struct ViewModelHandle(Vec<mpsc::Sender<()>>);

impl ViewModelHandle {
    pub fn new<Observer>(num_threads: usize, observer: Observer) -> (ViewModelHandle, ViewModel)
        where Observer: ViewModelObserver
    {
        // Create a new, empty view model.
        let starting_vm = ViewModel::new();

        // Wrap our view model and observer into a mutex and put that inside of an Arc.
        let inner = Arc::new(Mutex::new(Inner::new(starting_vm.clone(), observer)));

        // Create a vector to hold all the senders.
        let mut worker_channels = Vec::with_capacity(num_threads);

        for i in 0..num_threads {
            // Create a new channel pair and stash away the sending side.
            let (tx, rx) = mpsc::channel();
            worker_channels.push(tx);

            // Create a new thread worker, giving it a reference to the mutex-protected
            // Inner we created up above.
            let worker = ThreadWorker::new(i, inner.clone(), rx);

            // Spawn a new thread which runs "main" on the worker.
            thread::spawn(move || worker.main());
        }

        // Send back the handle and the initial view model state.
        (ViewModelHandle(worker_channels), starting_vm)
    }
}

Interfacing with Swift

For the sake of the length of this post, I’m going to skip most of the C
interface
and the Swift code the wraps, it as they are very similar
to what we discussed in Part 3. One new feature is that we can now
use rusty-cheddar to automatically generate the C header from
our Rust code (including copying doc comments!), which is fantastic both from the
“reduced manual work” and the “reduced chance of error due to the header and the
code getting out of sync” points of view.

The one bit of new interface technique I want to cover deals with the
view_model_observer struct that Swift fills in and gives to Rust. This is the
C interface:

struct view_model_observer {
    void* user;
    void (*destroy_user)(void* user);

    void (*inserted_item)(void* user, view_model* view_model, size_t index);
    void (*removed_item)(void* user, view_model* view_model, size_t index);
    void (*modified_item)(void* user, view_model* view_model, size_t index);
};

user and destroy_user are straight out of Part 3: we’re going to give Rust
some Swift object and a way to deallocate it when it’s finished. What do we
pass here though? We have a ViewModelHandle class in Swift that wraps up our
Rust view model handle. Remember that to pass a Swift object as a raw C
pointer, we have to convert it via Unmanaged<T>, which has
takeRetainedValue and takeUnretainedValue methods for dealing with the
Swift reference count. But both of these are problematic:

  • If we use takeRetainedValue to give the Rust ViewModelHandle a strong
    reference to our Swift ViewModelHandle, that creates a strong reference
    cycle and neither will ever be deallocated.
  • If we use takeUnretainedValue to give the Rust ViewModelHandle an
    “unsafe unretained” reference to our Swift ViewModelHandle, we risk Rust
    trying to call functions and pass that pointer around after the Swift object
    has been deallocated.

The typical solution to this in Swift apps would be to pass a weak reference,
but we can’t do that directly across a C interface. Instead, we can create a
Swift wrapper object that holds a weak reference to our Swift ViewModelHandle
and give Rust a strong reference to that wrapper object:

final class WeakHolder<T: AnyObject> {
    weak var object: T?

    init(_ object: T) {
        self.object = object
    }
}

final class ViewModelHandle {
    // The last-known state of the view model. Should only be read or written from the
    // iOS main thread (used to power a table view).
    var viewModel: ViewModel!

    init(numberOfWorkerThreads: Int) {
        // Create a pointer with a +1 reference count to a WeakHolder<ViewModelHandle>
        let weakSelf = UnsafeMutablePointer<Void>(Unmanaged.passRetained(WeakHolder(self)).toOpaque())

        // Pass it into the Rust layer
        let observer = view_model_observer(user: weakSelf,
                                           destroy_user: freeViewModelHandle,
                                           inserted_item: handleInsertedItem,
                                           removed_item: handleRemovedItem,
                                           modified_item: handleModifiedItem)

        ...
    }
}

We’ll also write some Swift glue code to translate the C callbacks we get into a proper Swift protocol that dispatch back onto the iOS main queue:

protocol ViewModelHandleObserver: class {
    func viewModelHandle(handle: ViewModelHandle, insertedItemAtIndex index: Int)
    func viewModelHandle(handle: ViewModelHandle, removedItemAtIndex index: Int)
    func viewModelHandle(handle: ViewModelHandle, modifiedItemAtIndex index: Int)
}

private func handleInsertedItem(ptr: UnsafeMutablePointer<Void>, viewModel: COpaquePointer, index: Int) {
    autoreleasepool {
        // Convert the pointer we get back into a WeakHolder<ViewModelHandle>
        let handle = Unmanaged<WeakHolder<ViewModelHandle>>.fromOpaque(COpaquePointer(ptr)).takeUnretainedValue()

        // Jump onto the main thread
        dispatch_async(dispatch_get_main_queue()) {
            // "Strongify" the reference to our ViewModelHandle (or return immediately if it's gone)
            guard let handle = handle.object else { return }

            // Update our viewModel property to the latest version we've been given
            handle.viewModel = ViewModel(viewModel)

            // Notify our observer about what changed
            handle.observer?.viewModelHandle(handle, insertedItemAtIndex: index)
        }
    }
}

// handleRemovedItem and handleModifiedItem are nearly identical

The Final App

We’ll let the user decide how many Rust threads should be created, and give them a way to
restart the entire process with a different number (nicely giving us a way to
verify that all of our cleanup code runs successfully). Each thread can insert
rows in the table view with the contents rust-thread-$THREAD_ID, and when
they modify a row they append -$THREAD_ID to whichever row they randomly
selected. After running for a few minutes with eight threads, this is what I get
(you’ll see different results):

The final app

Threading and Memory Concerns

There is no question that it could be very expensive to make a full copy of the entire view model state every time it’s changed. In the next post, we’ll explore
a completely different strategy to avoid that, but first let’s discuss some
intricacies of this one. A few natural questions would be:

  • Can we avoid copying the whole view model every time?
  • Why did we call the observer while we were holding a mutex locked?
  • Since we’re using a mutex, why do we need to make copies at all? Shouldn’t
    the mutex keep us safe?

The answer to the first question is “yes.” We could avoid copying the whole
view model every time, and there are lots of different ways we could accomplish
that. For example, we could send Swift the single value that changed each time,
and let it update its own copy itself. That would definitely be more
performant, but would have some cost in complexity—we’d need to maintain
code to update the view model in two different languages. This would also get
considerably trickier as our view model became more than just a list of
strings.

The answer to questions two and three are closely related. The mutex around our
Inner value protects us from data races; however, calling the observer
without the mutex locked or not giving copies of the data would both introduce
a different race condition that would lead to everyone’s favorite iOS
exception (courtesy of UITableView): “The number of rows contained in an
existing section after the update (…) must be equal to the number of rows
contained in that section before the update (…), plus or minus the number of
rows inserted or deleted from that section (… inserted, … deleted) and plus
or minus the number of rows moved into or out of that section (… moved in,
… moved out).”

Consider what happens the way we’ve written the app. The following diagram
shows the flow of view model updates with two background Rust threads, with
time moving from top to bottom:

Good locking

Whenever a background thread obtains the lock, it modifies the view model and
sends the updated state to the iOS main thread (via the observer callbacks).
Consider what could happen if we unlocked the mutex after modifying the view
model but before calling the observer:

Bad observer locking

If we unlock the mutex immediately after thread 0 modifies the view model, it’s
possible the OS could schedule thread 1 to start immediately, lock the mutex,
call the observer and then finally go back to executing thread 0. If that
happens, the iOS main thread will get out-of-order view model updates, leading
to the table view exception we all hate.

An almost identical issue occurs if we don’t send updated view models as part
of the observer messages but instead provide a way for iOS to ask Rust to lock
the mutex and inspect the current value of the view model. We could get an
“inserted row 0” callback from thread 0, then thread 1 could lock and update
the view model (inserting another row), then finally the main thread would
run again. It would expect that row 0 was just inserted, but it would see a
view model with two rows in it. Again, we’d get the table view exception.

It is generally unwise for a library to call back into other code while holding
a mutex locked, as that opens the door for deadlock if the “other code” (Swift,
in our case) calls back into the library again in some way that attempts to
lock the same mutex. We don’t have that problem here, though, because we don’t
expose any functions to Swift that try to lock the mutex—only the background
worker threads have access to it.

Next Steps

We finally have a working (albeit silly) iOS app that’s driven by Rust! In the
next post, we’ll recreate this same application using a different technique
that doesn’t need to make a full copy of the full view model when it’s changed
(and actually, doesn’t even need a mutex!). Stay tuned.

Thanks to Steve Klabnik for reviewing a draft of this post.

The post Building an iOS App in Rust, Part 4: A Basic View Model in Rust appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-4-a-basic-view-model-in-rust/feed/ 0
Building an iOS App in Rust, Part 3: Passing Owned Objects between Rust and iOS https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-3-passing-owned-objects-between-rust-and-ios/ https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-3-passing-owned-objects-between-rust-and-ios/#respond Wed, 02 Dec 2015 09:53:37 +0000 https://nerdranchighq.wpengine.com/blog/building-an-ios-app-in-rust-part-3-passing-owned-objects-between-rust-and-ios/ In Part 3 of our "Building an iOS App in Rust" series, we'll cover how to pass much more complex data than strings, and how to correctly and safely manage the ownership of that data.

The post Building an iOS App in Rust, Part 3: Passing Owned Objects between Rust and iOS appeared first on Big Nerd Ranch.

]]>

Editor’s note: This is the third post in our series on building an iOS app in Rust.

Welcome to Part 3 of our “Building an iOS App in Rust” series. This post
continues to build on the tools and details we set up in Part 1
and Part 2, so please refer back to those as needed. Part 1
walks you through setting up a cross-compiler built from the latest “unstable”
Rust compiler. The code in in this post uses a feature that was only recently
stabilized, so if you are following along with your own compiler toolchain,
note that you’ll need Rust 1.4 or newer.

Another major change is that we’re now using Swift 2, which added the
capability to pass Swift functions to C APIs that wanted function pointers. We no longer need to drop down into Objective-C to wrap our Rust code.

In Part 2, we ended with passing strings between Rust and Swift. Our
implementation worked but was not ideal in either direction:

  • When passing a Swift String into Rust via the c_string_to_rust function,
    the pointer we give to Rust is only valid for the duration of the function
    call. If we want to keep the string to do something with it later, we’d need
    to make a copy of the string data.
  • When passing a Rust string to Swift as we did in the get_string_from_rust
    function, the pointer we returned was valid only because it pointed to static
    constant data. We don’t currently have a way to pass a dynamically allocated
    string from Rust to Swift.

Both of these issues would be complete showstoppers for a real application. We
need a way to pass much more complex data than strings, and we’ll need to be
able to correctly and safely manage the ownership of that data.

As in Part 2, we will not be going over every line of code in detail, but all
of the code for this post is available on GitHub.

Ownership in Rust

Rust is a great language for a number of reasons, but the one feature where it
really shines is how it deals with ownership (who is responsible for
allocating and deallocating resources held by an instance) and borrowing (loaning out temporary access to an instance without giving away ownership. Much
has been written about this, with more detail than I can offer here. If you
want to learn more, check out the excellent Fearless Concurrency
with Rust blog post
as well as the Rust book’s sections
on Ownership, References and
Borrowing
and Lifetimes.

In pure Rust, the compiler makes sure you obey ownership rules that ensure all
resources will be cleaned up at the proper time. The same ownership rules
provide thread safety guarantees that we’ll discuss in a later post. When we’re
working with the foreign function interface (FFI) layer, we have to be much
more careful, as we’re interacting with a system outside of the Rust compiler’s
knowledge, so we don’t get nearly as much help.

The most basic things we want to be able to understand are (a) how long will an
object we’re passing between languages be valid (e.g., in
c_string_to_rust, the pointer is valid only for the function call), and (b)
who is responsible for cleaning it up (e.g., in c_string_to_rust, Swift will
free the string’s memory). For the rest of this post, we’re going to look at
how to pass ownership of an object across the boundary: creating an object in
Rust and giving responsibility for its cleanup to Swift and vice versa.

Giving Ownership of a Rust Instance to Swift

Most of the Rust types we’re going to create will not boil down to a primitive
or even a simple structure like RustByteSlice. For example, we could build a
Rust structure like this, which represents a label and some associated data:

// Automatically derive an implementation of the `Debug` trait so we can
// print instances of NamedData for debugging purposes.
#[derive(Debug)]
pub struct NamedData {
    name: String,
    data: Vec<i32>,
}

When we wrote the C header file for RustByteSlice, we put the C-compatible
definition of RustByteSlice directly in the header. We can’t do that here,
though: we don’t know what the in-memory layout of NamedData is, because
we don’t know what the internal data layouts of String or Vec<i32> are (nor
should we need to). If we want to create instances of this type and give them
to Swift, we can create a C interface for creation and destruction like this:

// Forward declare a struct but never specify its fields.
// We will only work with pointers to named_data.
struct named_data;

// Create a new instance of `named_data`.
// The caller is responsible for passing the returned pointer to
// named_data_destroy, or memory will be leaked.
struct named_data *named_data_new(void);

// Free a `named_data` instance returned by `named_data_new`.
void named_data_destroy(struct named_data *data);

The Swift side can’t use a struct named_data * directly; its bridged type,
COpaquePointer, makes that obvious. We’ll need to add functions to access the
properties we want to expose:

// Get the name of a `named_data`. The returned byte slice is valid until
// the `named_data` instance is destroyed.
struct RustByteSlice named_data_get_name(const struct named_data *data);

// Get the number of elements stored in `data`.
size_t named_data_count(const struct named_data *data);

On the Rust side, let’s start by implementing the Drop trait for NamedInit.
The drop function is called when a value goes out of scope; it’s analogous to
a destructor in C++ or deinit in Swift (although any value in Rust can
implement Drop, whereas there’s no way to have a deinit for Swift structs
or enums). We don’t actually need to do anything, but we can add a print
statement for our own understanding:

impl Drop for NamedData {
    fn drop(&mut self) {
        println!("{:?} is being deallocated", self);
    }
}

To implement named_data_new, we need to create an instance of NamedData
on the heap (if we created it on the stack, it would be destroyed as soon as
named_data_new returns), then return a raw pointer to the instance. Rust’s
standard library provides the Box type for heap-allocating instances.
Typically Rust’s RAII semantics would cause a Box-allocated instance
to be deallocated when the box goes out of scope, but we can use
Box::into_raw to cause the instance to be forgotten by the RAII system.

#[no_mangle]
pub extern fn named_data_new() -> *mut NamedData {
    // Create an instance of NamedData.
    let named_data = NamedData{
        name: "some data".to_string(),
        data: vec![1, 2, 3, 4, 5],
    };

    // Put named_data into a Box, which moves it onto the heap.
    let boxed_data = Box::new(named_data);

    // Convert our Box<NamedData> into a *mut NamedData. Rust is no longer
    // managing the destruction of boxed_data; we must (at some point in the
    // future) convert this pointer back into a Box<NamedData> so it can be
    // deallocated.
    Box::into_raw(boxed_data)
}

This will leak memory if we don’t later give the instance back to a Rust Box
for deallocation, but that’s exactly what we want! We created a heap-allocated
NamedData, got a pointer to it that we can give to Swift, and Rust will not
deallocate it out from under us. We can implement named_data_destroy that
will take a raw pointer to a NamedData, put it back into a Box, and let
that Box fall out of scope, causing the instance to be deallocated.

#[no_mangle]
pub unsafe extern fn named_data_destroy(data: *mut NamedData) {
    // Convert a *mut NamedData back into a Box<NamedData>.
    // This function is unsafe because the Rust compiler can't know
    // whether data is actually pointing to a boxed NamedData.
    //
    // Note that we don't actually have to do anything else or even
    // give the new Box a name - when we convert it back to a Box
    // and then don't use it, the Rust compiler will insert the
    // necessary code to drop it (deallocating the memory).
    let _ = Box::from_raw(data);
}

Implementing the two accessor functions is simpler; the only new bit here is
converting a raw *const NamedData into a Rust reference (&NamedData), which
requires an unsafe block (because we have to dereference the pointer, and the
Rust compiler can’t know whether the pointer is actually valid):

#[no_mangle]
pub extern fn named_data_get_name(named_data: *const NamedData) -> RustByteSlice {
    let named_data = unsafe { &*named_data };
    RustByteSlice::from(named_data.name.as_ref())
}

#[no_mangle]
pub extern fn named_data_count(named_data: *const NamedData) -> size_t {
    let named_data = unsafe { &*named_data };
    named_data.data.len() as size_t
}

There is an interesting point to make here about the use of unsafe. The Rust
compiler is extremely strict about safety, but it is sometimes necessary to use
unsafe to implement particular details. We should be careful what kind of API
we present, though. These functions are primarily intended to be used from
outside of Rust, but they could still be called by other Rust code. These
functions appear to the Rust compiler to be safe – it should be memory safe to
call these with any possible *const NamedData. But our implementation does
not check for NULL, in particular. This is very bad – we have an unsafe
function masquerading as a safe one. To fix this, we can move unsafe up from
just a block around the pointer dereference to a marker on the entire function:

#[no_mangle]
pub unsafe extern fn named_data_get_name(named_data: *const NamedData) -> RustByteSlice {
    let named_data = &*named_data;
    RustByteSlice::from(named_data.name.as_ref())
}

#[no_mangle]
pub unsafe extern fn named_data_count(named_data: *const NamedData) -> size_t {
    let named_data = &*named_data;
    named_data.data.len() as size_t
}

Now our functions are correctly marked for other Rust consumers – if they want
to call these functions, they can only do so from other unsafe code. We also
make use of a fairly common Rust idiom: let named_data = &*named_data;
creates a new binding for the named_data name which shadows the old
named_data. The new named_data has type &NamedData; the shadowed one that
we no longer can (or need to) access had type *const NamedData.

On the Swift side, we want to guarantee we’re not going to forget to pair every
call to named_data_new with a call to named_data_destroy, so we’ll
create a RustNamedData wrapper class with an appropriate deinit. This is
also a convenient place to put calls to the other accessor functions, as well:

class RustNamedData {
    private let raw: COpaquePointer

    init() {
        raw = named_data_new()
    }

    deinit {
        named_data_destroy(raw)
    }

    var name: String {
        let byteSlice = named_data_get_name(raw)
        return byteSlice.asString()!
    }

    var count: Int {
        return named_data_count(raw)
    }
}

We can create an instance of this class, print some properties, and see the
destruction happen as we expect:

let namedData = RustNamedData()
print("namedData.name = (namedData.name)")
print("namedData.count = (namedData.count)")

// Output from running the above snippet:
// namedData.name = some data
// namedData.count = 5
// NamedData { name: "some data", data: [1, 2, 3, 4, 5] } is being deallocated

Embedding Rust into another language is relatively straightforward. There are
no concerns about any runtime support like a garbage collector, so we just have
to give the host language (Swift) a way to create and destroy instances. Going
the other direction is a little trickier thanks to ARC.

Giving Ownership of a Swift Instance to Rust

Passing ownership of Swift objects down to Rust is problematic. We saw earlier
how to pass an ephemeral pointer to a String; we could easily do that with
other Swift types by making use of the withUnsafePointer Swift function.
However, pointers created by withUnsafePointer are only valid for the
duration of that function call, and we’re going to need a way to give Rust a
more permanent handle on Swift objects. In Swift 1, we would need to drop
down to Objective-C to solve this problem. Since Swift 2 added the ability
to pass Swift functions to APIs expecting C function pointers, we no longer
need to do that. We will still get our hands a little dirty, though.

Let’s start with the C interface each side is going to implement:

struct swift_object {
    void *user;
    void (*destroy)(void *user);
    void (*callback_with_int_arg)(void *user, int32_t arg);
};

void give_object_to_rust(struct swift_object object);

The swift_object struct has three fields:

  1. user is a void *; it will be a pointer to an instance of our Swift object.
  2. destroy is a C function pointer that will be called when Rust wants to destroy user.
  3. callback_with_int_arg is a C function pointer that Rust can call with a 32-bit signed integer argument.

Let’s create the Swift side of our Swift object:

class SwiftObject {
    deinit {
        print("SwiftObject being deallocated")
    }

    private func callbackWithArg(arg: Int) {
        print("SwiftObject: received callback with arg (arg)")
    }

    func sendToRust() {
        let ownedPointer = UnsafeMutablePointer<Void>(Unmanaged.passRetained(self).toOpaque())
        let wrapper = swift_object(
            user: ownedPointer,
            destroy: destroy,
            callback_with_int_arg: callback_with_int_arg)
        give_object_to_rust(wrapper)
    }
}

private func callback_with_int_arg(user: UnsafeMutablePointer<Void>, arg: Int32) {
    let obj: SwiftObject = Unmanaged.fromOpaque(COpaquePointer(user)).takeUnretainedValue()
    obj.callbackWithArg(Int(arg))
}

private func destroy(user: UnsafeMutablePointer<Void>) {
    let _ = Unmanaged<SwiftObject>.fromOpaque(COpaquePointer(user)).takeRetainedValue()
}

deinit and callbackWithArg are straightforward: we just want to see output
when they’re called. Most of the magic happens in sendToRust, so let’s break
that down:

let ownedPointer = UnsafeMutablePointer<Void>(Unmanaged.passRetained(self).toOpaque())

We have an instance of a Swift object (self), and the bridged form of our
swift_object struct is expecting an UnsafeMutablePointer<Void> for its
user field. We have to pass through two intermediate states to get there:

  • Unmanaged is Swift’s window into you taking more control over memory management. When you call Unmanaged.passRetained(self), you get back “an unmanaged reference with an unbalanced retain”. This is exactly what we want: the reference has now been retained, and we are responsible for releasing the object when we (or Rust) is finished with it.
  • Given an Unmanaged<SwiftObject>, we can call toOpaque() on it to get a COpaquePointer.
  • Given a COpaquePointer, we can finally create an UnsafeMutablePointer, the type we need to supply for user.

Now that we’ve converted self into an UnsafeMutablePointer<Void>, we can
build up an instance of the swift_object struct and call our
give_object_to_rust function:

let wrapper = swift_object(
    user: ownedPointer,
    destroy: destroy,
    callback_with_int_arg: callback_with_int_arg)
give_object_to_rust(wrapper)

The destroy and callback_with_int_arg arguments are private functions; let’s look
at those now.

callback_with_int_arg is given a user (the exact same UnsafeMutablePointer<Void> we
just created and an integer argument. The tricky bit here is converting user
back into a usable SwiftObject; we have to repeat the process we did above
but in reverse:

// UnsafeMutablePointer<Void> -> COpaquePointer
COpaquePointer(user)

// COpaquePointer -> Unmanaged<SwiftObject>
Unmanaged.fromOpaque(COpaquePointer(user))

// Unmanaged<SwiftObject> -> SwiftObject
Unmanaged.fromOpaque(COpaquePointer(user)).takeUnretainedValue()

Note that we call takeUnretainedValue(), not takeRetainedValue(), because
we do not want to modify the reference count of the underlying SwiftObject.
Now that we have a SwiftObject, we can call methods on it just like normal:

obj.callbackWithArg(Int(arg))

Finally, destroy is a one-liner that is almost identical to the first
line of callback_with_int_arg. The difference, as you probably expect, is that here we do
call takeRetainedValue(). This will decrement the reference count on the
underlying object, causing it to be deallocated (assuming Rust was holding the
only or last reference to it).

Now that the Swift side is ready, what does the Rust side look like? First,
let’s define the SwiftObject struct:

use libc::c_void;

#[repr(C)]
pub struct SwiftObject {
    user: *mut c_void,
    destroy: extern fn(user: *mut c_void),
    callback_with_int_arg: extern fn(user: *mut c_void, arg: i32),
}

This shouldn’t be too bad, if you’ve made it this far. C function pointers come
in as extern fn types, and we need to make sure the argument and return types
match. (If we wanted to allow these function pointers to be NULL, we would
use Option<extern fn(…)> instead, but we don’t need to do that for this
example.)

Now for give_object_to_rust. To make things interesting, we’ll start up a
thread, move the SwiftObject onto that thread, sleep for 1 second, and then
issue the callback into iOS. To tell Rust that it is safe for instances of
SwiftObject to be sent across threads, we’ll also need to add an
implementation of the (empty) Send trait. (The explanation for this is a
little long, and this post is already too long by half, so I’ll refer you to
the Rust book’s Concurrency chapter if you’re
curious.)

use std::thread;

unsafe impl Send for SwiftObject {}

#[no_mangle]
pub extern fn give_object_to_rust(obj: SwiftObject) {
    println!("moving SwiftObject onto a new thread created by Rust");
    thread::spawn(move||{
        thread::sleep_ms(1000);
        (obj.callback_with_int_arg)(obj.user, 10);
        (obj.destroy)(obj.user);
    });
}

If we run this, we’ll find everything working; we get the following log, with a
1 second delay between the 2nd and 3rd lines:

moving SwiftObject onto a new thread created by Rust
SwiftObject: received callback with arg 10
SwiftObject being deallocated

You can also set a breakpoint in SwiftObject.callbackWithArg and see that
the callback is happening off of the main thread.

However, there is the unsightly bit of having to manually call
obj.destroy. This is Rust – we should not need to do manual resource
management! Earlier, we implemented the Drop trait on NamedData. Let’s try
to do the same thing here. We can move the call to destroy into drop,
which means Rust will put the call at exactly the right place (whenever our
SwiftObject falls out of scope):

impl Drop for SwiftObject {
    fn drop(&mut self) {
        (self.destroy)(self.user);
    }
}

#[no_mangle]
pub extern fn give_object_to_rust(obj: SwiftObject) {
    println!("moving SwiftObject onto a new thread created by Rust");
    thread::spawn(move||{
        thread::sleep_ms(1000);
        (obj.callback_with_int_arg)(obj.user, 10);
        /* (obj.destroy)(obj.user); */
    });
}

This compiles, but with a pretty scary warning:

src/swift_ownership_to_rust.rs:13:1: 17:2 warning: implementing Drop adds hidden state to types, possibly conflicting with `#[repr(C)]`, #[warn(drop_with_repr_extern)] on by default
src/swift_ownership_to_rust.rs:13 impl Drop for SwiftObject {
src/swift_ownership_to_rust.rs:14     fn drop(&mut self) {
src/swift_ownership_to_rust.rs:15         (self.destroy)(self.user);
src/swift_ownership_to_rust.rs:16     }
src/swift_ownership_to_rust.rs:17 }

It turns out the warning is scary-sounding for a reason: running this code now
crashes!

Explaining what’s going on is a little involved, but I’ll try to summarize.
Currently, when you implement Drop on a struct, the Rust compiler inserts a
hidden field into that struct that it uses to track whether or not it needs to
call drop. This is just a limitation of the current implementation; there are
an accepted RFC and an open issue
that will address this. For now, however, this hidden field changes the size
of SwiftObject. The Rust compiler is warning us that we said we wanted an
in-memory representation compatible with C, but that isn’t happening because of
this hidden field.

Luckily, fixing this is easy. We can wrap SwiftObject up in a new Rust type
and implement Drop on that type instead. We’ll use Rust’s newtype syntax
for a struct-that-only-exists-to-wrap-another-type:

use std::ops::Deref;

struct SwiftObjectWrapper(SwiftObject);

impl Deref for SwiftObjectWrapper {
    type Target = SwiftObject;

    fn deref(&self) -> &SwiftObject {
        &self.0
    }
}

impl Drop for SwiftObjectWrapper {
    fn drop(&mut self) {
        (self.destroy)(self.user);
    }
}

There’s another new thing here: we implemented the Deref trait.
This lets us freely access the fields (and methods, if there were any) on a
SwiftObjectWrapper’s inner SwiftObject. Finally, we need to update our
give_object_to_rust implementation to wrap the struct Swift gives us into a
SwiftObjectWrapper, and move that wrapper onto the background thread:

#[no_mangle]
pub extern fn give_object_to_rust(obj: SwiftObject) {
    println!("moving SwiftObject onto a new thread created by Rust");
    let obj = SwiftObjectWrapper(obj);
    thread::spawn(move||{
        thread::sleep_ms(1000);
        (obj.callback_with_int_arg)(obj.user, 10);
    });
}

This compiles (and runs) without warning or error, and we no long have to worry
about calling destroy: Rust will insert the call for us at precisely the
right moment.

Next Steps

We’ve covered a lot in this post! We now know how to pass all kinds of things from Rust to Swift and from Swift to Rust. In
the next couple of posts, we’ll start to take steps towards a more realistic
example and explore two different ways to implement view models in Rust that
can be used from Swift.

All the code, plus the extra stuff necessary to get it running (like an Xcode
project file), is available on GitHub.

The post Building an iOS App in Rust, Part 3: Passing Owned Objects between Rust and iOS appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-3-passing-owned-objects-between-rust-and-ios/feed/ 0
Building an iOS App in Rust, Part 2: Passing Primitive Data Between Rust and iOS https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-2-passing-primitive-data-between-rust-and-ios/ https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-2-passing-primitive-data-between-rust-and-ios/#respond Thu, 20 Aug 2015 09:53:37 +0000 https://nerdranchighq.wpengine.com/blog/building-an-ios-app-in-rust-part-2-passing-primitive-data-between-rust-and-ios/ Last time, we built a simple "Hello, World!" library in Rust and successfully linked it into an iOS app. This time, we're going to explore more of Rust's FFI, or [foreign function interface](http://en.wikipedia.org/wiki/Foreign_function_interface), layer. The Rust book has an excellent [chapter on Rust's FFI facilities](https://doc.rust-lang.org/book/ffi.html); however, it is almost exclusively about how to call C libraries *from* Rust, whereas we want to go the other direction and call Rust from Swift.

The post Building an iOS App in Rust, Part 2: Passing Primitive Data Between Rust and iOS appeared first on Big Nerd Ranch.

]]>

Editor’s note: This is the second post in our series on building an iOS app in Rust.

Welcome to Part 2 of our “Building an iOS App in Rust” series! If you
haven’t read Part 1 already, please do that. Running the code in this
post will require you to have set up a Rust toolchain for iOS.

Last time, we built a simple “Hello, World!” library in Rust and successfully
linked it into an iOS app. This time, we’re going to explore more of Rust’s
FFI, or foreign function interface, layer. The Rust book
has an excellent chapter on Rust’s FFI facilities; however, it is
almost exclusively about how to call C libraries from Rust, whereas we want
to go the other direction and call Rust from Swift.

Unlike Part 1, I’m not going to walk you through generating a Rust library and
an iOS project to run all the sample code in this post. The code is hosted
on GitHub
, so you can check it out at your leisure.

Note: In Part 1, we scoped out a five-part plan for this series, and Part 2
was supposed to be “Passing Data Between Rust and iOS.” It started to get
a little long, though, so we’ve split the data passing into two. There will
be more than five parts in the end.

Designing an API for our Rust layer

API design is hard in any language. We’re throwing a peculiar wrench into
things with this project:

  • We’re writing a library in Rust, but need to expose its public interface in a
    C header, so we’re restricted to data types that are compatible with C. This
    means, for example, no generics or enums that hold
    data
    . Swift supports both of those itself, but not in a way
    that’s compatible with Rust.
  • On the Swift side, it looks like we’re talking to a C library. This will be a
    little painful at times, too, as anyone who has worked with Core Foundation
    in Swift has already experienced.
  • We’re going to make use of Swift 2’s ability to pass pointers to Swift functions
    as C function pointers. To accomplish all the parts of this post in Swift 1,
    you would need to drop down to Objective-C some.

Basically, we have two modern, advanced languages talking to each other… but they
both think they’re talking to C. Great.

At CppCon 2014, Stefanus DuToit gave an excellent talk called Hourglass
Interfaces for C++ APIs
. It’s well worth watching, even
if you don’t speak C++. The talk is largely a sales pitch to C++ library
writers, giving them reasons to create, and advice on creating, C-compatible
wrappers for their APIs. We’re going to follow a lot of his advice when
wrapping Rust code:

  • We’ll make use of pointers to forward-declared C structs, also known as
    opaque pointers. This will allow us to send arbitrarily
    complex data structures across the language boundary. Rust and Swift don’t
    generally know how each other’s data structures are laid out in memory, but
    both of them know how to pass pointers around.
  • We’ll avoid the plain C integral types like int and long in favor of
    explictly sized types like int32_t and uint8_t. We would end up doing
    this anyway in Rust: idiomatic Rust doesn’t use a basic integer type whose
    size changes depending on your target architecture, like int in C or Int
    in Swift. Rust tends to use i32 instead, which is always guaranteed to be
    32 bits.
  • We’ll use C function pointers to handle callbacks between the two languages.
  • Breaking a little with the Hourglass Interfaces talk, we will use a couple of
    very basic non-opaque structs. Rust has an annotation that tells it to lay
    out structs in memory just like a C compiler would, so Rust and Swift will
    agree on where to find the struct members.

Let’s start with exchanging primitive numbers.

Sending primitives between Rust and iOS

In Part 1 of this series, you already saw a Rust function that returns an
i32, a 32-bit signed integer:

#[no_mangle]
pub extern fn rust_hello_world() -> i32 {
    println!("Hello, I'm in Rust code! I'm about to return 10.");
    10
}

Here are more examples of Rust functions that take and return various
primitives:

// C declaration: int32_t return_int32(void);
#[no_mangle]
pub extern fn return_int32() -> i32 {
    10
}

// C declaration: uint16_t triple_a_uint16(uint16_t x);
#[no_mangle]
pub extern fn triple_a_uint16(x: u16) -> u16 {
    x * 3
}

// C declaration: float return_float(void);
#[no_mangle]
pub extern fn return_float() -> f32 {
    10.0
}

// C declaration: double average_two_doubles(double x, double y);
#[no_mangle]
pub extern fn average_two_doubles(x: f64, y: f64) -> f64 {
    (x + y) / 2.0
}

Rust’s i32 is C’s int32_t is Swift’s Int32. All the normal sizes (8, 16,
32, 64) are available in all three languages, and unsigned variants are as well
(u32 <-> uint32_t <-> UInt32, etc.). Swift’s Float is Rust’s f32, and
Swift’s Double is Rust’s f64.

There’s one last primitive I want to discuss. I said earlier that Rust didn’t
have a default integer type that changes size based on the target platform
(like how Swift’s Int can be either 32 or 64 bits). That’s true, but it does
have something similar that comes up in several important Rust APIs: usize is
a “pointer-sized unsigned integer.” Rust uses usize for things like the
length of a string, or the number of elements in an array.

Rust uses an unsigned integer for things like array length; Swift, on the other
hand, uses Int, which is signed. To reconcile this, we’ll pass Rust’s usize
as a C size_t. Because size_t is used in C for things like length, Swift
bridges it in as Int. We’ll need to do a little casting on the Rust side, but
that’s ok. Here is a Rust function that works with size_t, internally
converting to usize:

use libc::size_t;

#[no_mangle]
pub extern fn sum_sizes(x: size_t, y: size_t) -> size_t {
    let x_usize = x as usize;
    let y_usize = y as usize;
    (x_usize + y_usize) as size_t
}

The first line is importing a type, size_t, from the libc
crate
. libc provides platform-specific bindings to native C data
types, like size_t (and things like void – that’ll come up in a few
minutes).

In the body of the function, we use the as keyword to cast between
size_t and usize. Like Swift, Rust doesn’t do implicit conversions, so we
must be explicit.

We can call all of these from Swift just like regular functions:

func exercisePrimitives() {
    let a: Int32 = return_int32()
    let b: UInt16 = triple_a_uint16(10)
    let c: Float = return_float()
    let d: Double = average_two_doubles(10, 20)
    let e: Int = sum_sizes(20, 30)
    print("primitives: (a) (b) (c) (d) (e)")
}

Sending strings from iOS to Rust

In the modern Unicode world, strings are surprisingly complex. Rust’s built-in
string types are always guaranteed to hold valid UTF-8 encoded data. This means
you can’t create a string out of arbitrary bytes unchecked; instead, you can
use the std::str::from_utf8 function to attempt to interpret
a slice of bytes into a string. from_utf8 returns a Result<&str,
Utf8Error>
type: an enum that is either Ok with an associated
&str value or Err with an associated Utf8Error value.

Here’s a Rust function that uses Rust’s match construct (analogous to Swift’s
switch) to unpack the returned Result:

use std::str;

// Take a slice of bytes and try to print it as a UTF-8 string.
fn print_byte_slice_as_utf8(bytes: &[u8]) {
    match str::from_utf8(bytes) {
        Ok(s)    => println!("got {}", s),
        Err(err) => println!("invalid UTF-8 data: {}", err),
    }
}

Note that this function is not marked pub or extern and doesn’t have the
#[no_mangle] attribute. Without pub, it’s a private function: we’re going
to use it internally, but not expose it to Swift.

Let’s write a Rust function that takes a raw pointer and a length in bytes,
converts it to a Rust slice, and calls our print_byte_slice_as_utf8 function:

use std::slice;

// C declaration: void utf8_bytes_to_rust(const uint8_t *bytes, size_t len);
#[no_mangle]
pub extern fn utf8_bytes_to_rust(bytes: *const u8, len: size_t) {
    let byte_slice = unsafe { slice::from_raw_parts(bytes, len as usize) };
    print_byte_slice_as_utf8(byte_slice);
}

The function declaration is straightforward. We take two arguments: the first
is a pointer to 8-bit unsigned integers (i.e., bytes), and the second is the
length. This is standard fare for passing around arrays in C.

The first line of the function body is a little hairy; let’s unpack it from the
inside out:

  • slice::from_raw_parts(bytes, len as usize) passes our two arguments to the std::slice::from_raw_parts function from Rust’s standard library. That function returns a &[u8], a slice of bytes.
  • unsafe { … } is our first encounter with Rust’s unsafe keyword. One of Rust’s claims to fame is memory safety: in pure, safe Rust, it’s impossible to ever access uninitialized or invalid memory. Unfortunately, when we’re calling Rust from another language, none of those guarantees exist. Someone might pass NULL as the argument to bytes, for example. from_raw_parts is marked as an unsafe function because the compiler can’t guarantee (at compile time) that the pointer and length you’re providing are actually valid. It also doesn’t know how long the pointer will be valid. Because both of these issues are giant holes that could cause our program to crash and burn, we’re forced to wrap the call in an unsafe block to tell the compiler, “I know this isn’t safe, but I want you to go ahead and do it anyway. I promise it’ll be okay.”
  • Finally, we store the returned slice into the byte_slice variable. Rust, like Swift, supports type inference; we could’ve written let byte_slice: &[u8]= … if we wanted to.

To call this function from Swift, we need to convert a String into
UTF-8-encoded data, then pass the data pointer and length:

let myString = "Hello from Swift"
let data = myString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
utf8_bytes_to_rust(UnsafePointer<UInt8>(data.bytes), data.length)

Rust and Swift both know how to work with old style, null-terminated C strings,
as well. Here’s the Rust side:

use std::ffi::CStr;

// C declaration: void c_string_to_rust(const char *null_terminated_string);
#[no_mangle]
pub extern fn c_string_to_rust(null_terminated_string: *const c_char) {
    let c_str: &CStr = unsafe { CStr::from_ptr(null_terminated_string) };
    let byte_slice: &[u8] = c_str.to_bytes();
    print_byte_slice_as_utf8(byte_slice);
}

This time, we use the std::ffi::CStr type, which represents a
“borrowed” C string. (We’ll talk more about borrowing and ownership shortly;
for now, think of it as “a string someone else is responsible for
deallocating”.) Like from_raw_parts, CStr::from_ptr is an unsafe function
because the Rust compiler can’t know at compile time whether the argument is
actually a valid null-terminated string.

Once we have a &CStr, we can use its to_bytes() method to get a view into
it as a slice of bytes, and then we can call our same
print_byte_slice_as_utf8 function. Note that no memory copies happen in this
function: the first line just wraps up the pointer into a new Rust type, and
the second line gives us a view into those same bytes.

Calling this function from Swift is trivial: Swift will automatically convert
Strings into C-style strings when you try to pass them to functions that take
const char *:

let myString = "Hello from Swift"
c_string_to_rust(myString)

Sending strings from Rust to iOS

I mentioned earlier that Rust stores strings encoded as UTF-8. This does not
include a C-style null-terminating byte. The cleanest way to pass a Rust string
out across the FFI boundary is to pass out an array of bytes and a length. We
could write a C interface that needs to return both, but then we have the
always-slightly-unwieldy output pointer type; e.g.,

// Unwieldy: Return a pointer to bytes and fill in `len` with its length.
const uint8_t *get_string_from_rust(size_t *len);

Instead, we’ll define a Rust struct and tell the compiler to use a binary
representation compatible with C:

#[repr(C)]
pub struct RustByteSlice {
    pub bytes: *const u8,
    pub len: size_t,
}

Now our C interface looks like this, where we pass back a small, 16-byte
structure by value:

struct RustByteSlice {
    const uint8_t *bytes;
    size_t len;
};

struct RustByteSlice get_string_from_rust(void);

Finally, the Rust implementation:

#[no_mangle]
pub extern fn get_string_from_rust() -> RustByteSlice {
    let s = "This is a string from Rust.";
    RustByteSlice{
        bytes: s.as_ptr(),
        len: s.len() as size_t,
    }
}

We used type inference, but the type of s is &str; that is, a “borrowed
string” (more on “borrowed” momentarily). Two methods on &str are
.as_ptr(), which returns a pointer to the string’s bytes, and .len(), which
returns the number of bytes in the string. Note that this is different from
Swift, which does not define a String’s length. Swift provides several
different views into the components of a String; see Strings in Swift
2.0
on the official Swift blog for details.

Did you notice anything unusual about the Rust implementation? Even though
we’re trafficking in raw C pointers, we never had to use the unsafe keyword.
Why not? The Rust compiler can statically guarantee that everything inside the
get_string_from_rust function is safe. It’s perfectly safe to create a raw
pointer to s’s bytes; in general, it’s perfectly safe to create and pass
around raw pointers. Dereferencing pointers is unsafe, but
get_string_from_rust doesn’t do that itself. Presumably whoever calls
get_string_from_rust is going to dereference the returned pointer, but that’s
their problem to deal with.

That does bring up a question, though. How long is the pointer
get_string_from_rust returns going to be valid? Is it really safe for Swift
to dereference it? Does someone need to deallocate it? (How would they? free,
or some other function?)

To answer that, we need the full type of s. A few paragraphs ago, I said it
was &str, but that’s only part of the story. All Rust references have an
associated lifetime. Most of the time you don’t have to work with lifetimes
yourself, as the Rust compiler can infer them. Our s variable actually has a
special lifetime. Because we initialized s with a string literal, its full
type is &'static str. The static lifetime outlives all other lifetimes. For
us, this means that the string s points to will always be valid (similar to a
static const char * string in a C program). It is safe for Swift to access
the pointer we return at any time, as it will always point to the bytes making
up the string This is a string from Rust..

So how do we call this function from Swift? The RustByteSlice struct gets
bridged in as this Swift type:

struct RustByteSlice {
    var bytes: UnsafePointer<Int8>
    var len: Int
}

We can write an extension on this bridged type to convert to an
UnsafeBufferPointer<UInt8> (which will always succeed) and to a String
(which will only succeed if the byte slice contains valid data according to the
expected encoding):

extension RustByteSlice {
    func asUnsafeBufferPointer() -> UnsafeBufferPointer<UInt8> {
        return UnsafeBufferPointer(start: bytes, count: len)
    }

    func asString(encoding: NSStringEncoding = NSUTF8StringEncoding) -> String? {
        return String(bytes: asUnsafeBufferPointer(), encoding: encoding)
    }
}

Now we can ask Rust for a String, and print it:

let rustString = get_string_from_rust()

if let stringFromRust = rustString.asString() {
    print("got a string from Rust: (stringFromRust)")
} else {
    print("Could not parse Rust string as UTF-8")
}

Next Steps

So far, all the data passing we’ve seen is quite limited. We can pass around
primitives without any problem: in Rust and Swift (as well as C and a host of
other languages), primitives are passed by value. Our string-passing functions
are more problematic:

  • In the Swift-to-Rust functions utf8_bytes_to_rust and c_string_to_rust, Swift owns the memory backing the string. The pointer it gives to Rust is valid for the duration of the function call, but may not be valid afterwards. If Rust were to squirrel away the pointer Swift gives it and try to access it later, Bad Things™ will probably happen.
  • Our Rust-to-Swift function get_string_from_rust is safe, but only because it’s passing a pointer to a static string.

Both directions are insufficient for building a real library. We need the ability to pass objects that live for longer than one function call, but we need the flexibility to work with dynamic data. Ultimately, we need to be able to reason about the ownership of our objects, and that will be the subject of the next post.

Editor’s note: Be sure to check out Part 3 of the series, where John covers how to pass more complex data than strings, and how to correctly and safely manage ownership of that data.

The post Building an iOS App in Rust, Part 2: Passing Primitive Data Between Rust and iOS appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-2-passing-primitive-data-between-rust-and-ios/feed/ 0
Building an iOS App in Rust, Part 1: Getting Started with Rust https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-1-getting-started-with-rust/ https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-1-getting-started-with-rust/#respond Sun, 07 Jun 2015 09:53:37 +0000 https://nerdranchighq.wpengine.com/blog/building-an-ios-app-in-rust-part-1-getting-started-with-rust/ If you're developing an app on multiple platforms, you'll often face duplicate work. C++ is the reigning king of the hill for portable, native library development, but there's a new challenger in Rust.

The post Building an iOS App in Rust, Part 1: Getting Started with Rust appeared first on Big Nerd Ranch.

]]>

Editor’s note: This is the first post in our series on building an iOS app in Rust.

The vast majority of apps that get developed for iOS and Android are written in
the native languages provided by the platform: Swift or Objective-C on iOS, and
Java on Android. I don’t expect that to change any time soon; however,
sometimes there’s a need for something more.

If you’re developing an app on multiple platforms more or less independently,
you’ll face certain challenges. Functionality will be duplicated (obviously), which means
you have two different codebases that need to be maintained. Bugs can crop up
on one platform or the other or both, and new features have to be added to
each. An alternative approach, which Dropbox talked about at last year’s
UIKonf and CppCon (video 1, video
2
), is to develop a library that can be shared by both
platforms.

Developing a cross-platform library is challenging for a number of reasons, not
the least of which that the choice of language is pretty limited. There are
some tools, like Google’s J2ObjC, which allow you to write in one
platform’s language (Java, in this case) and have it automatically translated
into another platform’s language (Objective-C). A more traditional approach is
to develop in C or C++, languages that are portable to both platforms and that
can be called by both platforms.

I’m not going to try and sell you on the merits of going down this road—there
are big pros and big cons. I suspect that this approach is probably the wrong
one for most applications, but it’s still a very interesting area to explore.
C++ is the reigning king of the hill for portable, native library development,
but there’s a new challenger with an exciting amount of development behind it.

Rust describes itself as “a systems programming language that runs
blazingly fast, prevents almost all crashes and eliminates data races.” It’s
been in development for quite a while (about eight years, at the time of this
writing), and the Rust team released version 1.0 on May 15 of this year.

Rust is often compared with Go (probably because they entered the public eye around the same
time and both described themselves as systems programming languages), but the
comparison isn’t really fair to either: they have very different aims in mind.
Rust’s goal is to be a safer alternative to C++ without giving up the control
and performance that C++ provides.

Roadmap

This post is the first in a (long) series. We’re going to end up with a simple but nontrivial app that can ask Flickr for its recent photos, display
thumbnails in a UICollectionView and show the full image when a thumbnail
is tapped:

Rustorama demo

The trick is that we’re going to put all the smarts in the Rust layer. We’ll
roughly follow an MVVM (Model—View—View Model) architecture where the Model
and View Model layers are implemented in Rust, and the iOS side is just the
View layer. (This app is a variant of one that you’ll build while going through the
next edition of our iOS Programming Guide, to be
published in the second half of 2015.)

While the app is simple, we’ll touch on a lot of advanced topics getting Rust
and iOS to play nicely together. Here’s the plan for this blog series:

  1. Getting Started with Rust on iOS (that’s this post)
  2. Passing Data between Rust and iOS
  3. Sharing a View Model between Rust and iOS
  4. Writing a Flickr Client in Rust
  5. Tying it All Together: Rustorama

I’ll cover some basic Rust syntax as we go through the post, but if this is
your first experience with the language, consider reading through the Rust
book
. I’ll be glossing over some fairly advanced things in the
later posts out of necessity.

Installing Rust with multirust

This section assumes you’re running Mac OS X and have not installed Rust. If
either of those isn’t true, you’ll need to tweak these instructions.

There are three different versions of the Rust compiler available at any given
time: stable, beta and nightly. Every six weeks, the current nightly becomes
the new beta and the current beta becomes the new stable; this is called the
six-week train model.

A slick tool for managing multiple Rust installations is
multirust. We’ll use it to manage a version of the Rust compiler
for targeting iOS.

Go ahead and install multirust and set the nightly as your default Rust
compiler. (I’ve omitted copying the instructions from the multirust repository
in case they change in the future, but at the moment, there’s a
one-liner
you can run to set everything up.) You
should be able to replicate the following, although your build dates and
version hashes will be different:

$ multirust show-default
multirust: default toolchain: nightly
multirust: default location: /Users/john/.multirust/toolchains/nightly

rustc 1.1.0-nightly (c42c1e7a6 2015-05-02) (built 2015-05-01)
cargo 0.2.0-nightly (efb482d 2015-04-30) (built 2015-05-01)

$ rustc -V
rustc 1.1.0-nightly (c42c1e7a6 2015-05-02) (built 2015-05-01)

Try a “Hello, World” program:

$ cat > hello-world.rs
fn main() {
    println!("Hello, world!");
}
<Ctrl-D>
$ rustc hello-world.rs
$ ./hello-world
Hello, world!

Building a Cross Compiler

This part is not for the faint of time: this will take at least an hour, maybe
a few. We need to build a Rust toolchain that can create executables for all
five iOS platforms: armv7, armv7s, arm64 and the 32- and 64-bit simulators.
We’re going to build off of the master branch, the same as the nightly
releases.

First, clone the Rust compiler’s repository and get its
submodules (this assumes you have SSH set up with Github; feel free to clone
however you normally would):

$ git clone git@github.com:rust-lang/rust.git
$ cd rust
$ git submodule update --init --recursive

Next, create a subdirectory for all the build artifacts and cd into it:

$ mkdir build
$ cd build

Finally, configure the build to target all five architectures, and set up an
appropriate installation prefix. In the following, we’ll install to
our home directory:

$ ../configure --target=armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,aarch64-apple-ios,x86_64-apple-ios --prefix=$HOME/rustc-ios
... snipping lots of output ...

At last, start the build:

$ make -j8 && make install

Go watch a movie or something; come back when your laptop fans stop spinning.

All done? Let’s tell multirust about your brand new toolchain, naming it ios:

$ multirust update ios --link-local $HOME/rustc-ios
multirust: creating link from /Users/john/rustc-ios

One final cleanup step. multirust expects to be able to find
Cargo, Rust’s package manager and build tool extraordinaire, but
we’ve only installed the Rust compiler itself. We don’t really need to go and
build Cargo, because the Rust nightly you installed in the previous section
also installed Cargo. Instead, we can create a symlink in the right place:

$ ln -s $HOME/.multirust/toolchains/nightly/bin/cargo $HOME/rustc-ios/bin

Ask multirust to run your ios version of rustc and Cargo, just to make sure
all is well:

$ multirust run ios rustc -V
rustc 1.1.0-dev (ce1150b9f 2015-05-04) (built 2015-05-03)
$ multirust run ios cargo -V
cargo 0.2.0-nightly (efb482d 2015-04-30) (built 2015-05-01)

Hello, World: Building the Rust Library

Now that all the painful waiting is done, let’s get to the fun part: writing a
Rust library and calling it from an iOS app. Create a clean working space
somewhere, and create directories to hold the Rust component and the iOS
component:

$ mkdir -p rust-ios-part-1/{ios,rust}
$ cd rust-ios-part-1

You probably noticed above that we were able to use multirust run ios ... to
run commands from the ios toolchain we installed. It would be awfully tedious
to type that every time, so multirust provides a directory-level override. Set
that up now, so that any Rust commands you issue in this directory (or any
descendent directory) will use your ios toolchain:

$ multirust override ios
multirust: using existing install for 'ios'
multirust: override toolchain for '/Users/john/rust-ios-app-part-1' set to 'ios'

We’ll build the Rust library first. Change into your rust directory and tell
Cargo to create a new library called hello for you:

$ cd rust
$ cargo new hello
$ cd hello

If you dig around under hello, you’ll find two files:

  • Cargo.toml is the manifest file describing your library. Cargo.toml is like a Rust-specific Makefile. It contains the names of your input files and any library or executable targets your project defines, as well as any dependencies your project uses.

  • src/lib.rs is the placeholder file created for you. This is where we’ll put whatever Rust code we write. (In later posts we’ll use more files, but this one is fine for “Hello, world.”)

Let’s start by updating src/lib.rs. There are Rust plugins for many popular editors; Google around for yours if you’d like. Delete the default code in src/lib.rs and replace it with this:

#[no_mangle]
pub extern fn rust_hello_world() -> i32 {
    println!("Hello, I'm in Rust code! I'm about to return 10.");
    10
}

Walking through each line:

  1. #[no_mangle] tells the Rust compiler not to perform its default name mangling on the function name. This will result in the rust_hello_world symbol being exported just like it would if it had been written in C.
  2. pub marks the function as public, i.e., callable by code outside of this library. extern fn tells the Rust compiler to use C calling conventions for this function, meaning any language that knows how to call C functions can now call this Rust function. rust_hello_world() is the name of the function; the empty parentheses indicate it takes no arguments. -> i32 states that the return value of this function is an i32, i.e., a 32-bit signed integer.
  3. println!("..."); will print the string on stdout. It’s analogous to
    Swift’s println function. (The ! means that println! in Rust is actually a macro, but that’s not important for our purposes.)
  4. 10, as the last line of the function without a semicolon, is the value
    returned by the function. Rust does have a return keyword, so we could have
    written return 10; instead, but that isn’t idiomatic. The Rust book’s
    functions chapter discusses this in more detail.

At this point, if you were developing a normal Rust library, you could build it
via cargo build and go about your merry way. We have a few more steps to
take to build a library suitable for iOS, though. By default, Cargo will create
libhello.rlib, where rlib stands for “Rust library.” We need a traditional
static library, so update Cargo.toml, adding the [lib] section below:

[package]
name = "hello"
version = "0.1.0"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"]

[lib]
name = "hello"
crate-type = ["staticlib"]

Now we can tell Cargo to build a static library, and we’ll specify that we want
one for the 64-bit iOS simulator:

$ cargo build --target x86_64-apple-ios
   Compiling hello v0.1.0 (file:///Users/john/github/bignerdranch/rust-ios-app-part-1/rust/hello)
note: link against the following native artifacts when linking against this static library
note: the order and any duplication can be significant on some platforms, and so may need to be preserved
note: library: System
note: library: objc
note: framework: Foundation
note: framework: Security
note: library: pthread
note: library: c
note: library: m
$ ls target/x86_64-apple-ios/debug/
build/      deps/       examples/   libhello.a  native/

For real development, we’ll actually want to use lipo to create a fat library
for all five iOS architectures. That isn’t something currently supported by
Cargo, so there is a Makefile in the repo for this blog post that
will tell Cargo to build all five architectures and then combine them into a
fat library.

There’s one last thing we need: a C header file that we can import on the iOS
side. There isn’t a tool (yet) for creating C headers for a
Rust library, so we will create a header manually. Still in your rust
directory, create hello.h and give it the following contents:

#pragma once

#include <stdint.h>

int32_t rust_hello_world(void);

This type signature matches our Rust function above: it takes no arguments and
returns a 32-bit signed integer.

Hello, World: Building the iOS App

Hop into Xcode and create a new Single-View project. Put it into the
rust-ios-part-1/ios directory you created above. I’ll assume you want to use
Swift; if you’re using Objective-C, things are actually simpler, so you can
probably manage just fine.

Find the hello.h and libhello.a files you created in the previous section,
and drag them both into your Xcode project. (Make sure you grab the
libhello.a under target/x86_64-apple-ios/debug, or the one you created
using the Makefile, if you did that.) In order for your Swift code to be able
to see hello.h, you need to include it in your app’s bridging header. By
default, Swift projects don’t have one. You can either create one
manually
or add a new class to your project, select
Objective-C as the language, click “Yes” when Xcode asks if you want a bridging
header, then delete the Objective-C files.

Once you have a bridging header, add hello.h to it:

//
//  Use this file to import your target's public headers that you would like
//  to expose to Swift.
//
#import "hello.h"

Open up AppDelegate.swift, and try calling your Rust function:

func application(application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?)
    -> Bool
{
    let result = rust_hello_world()
    println("I called Rust and got (result)")
    return true
}

Try to build and run your app. You’ll need to have a 64-bit simulator selected,
such as the iPhone 6 simulator. If you get a linker error about missing the
symbol _rust_hello_world, make sure you added the correct libhello.a to
your app target.

You should see the following in the Xcode console:

Hello, I'm in Rust code! I'm about to return 10.
I called Rust and got 10

Congratulations! You’ve written a Rust library and used it on iOS!

Next Steps

In the next post, we’ll build on all the setup you’ve done. We’ll talk about
how to pass non-primitive data types like strings to and from Rust, as well as
how to pass more complicated data structures and objects. Stay tuned!

All the code, both Rust and Swift, from this post is on GitHub.

Editor’s note: Be sure to check out the other posts in this series: Part 2, Part 3.

The post Building an iOS App in Rust, Part 1: Getting Started with Rust appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/building-an-ios-app-in-rust-part-1-getting-started-with-rust/feed/ 0
Locking in Swift: Helping Protect Me from Myself https://bignerdranch.com/blog/locking-in-swift-helping-protect-me-from-myself/ https://bignerdranch.com/blog/locking-in-swift-helping-protect-me-from-myself/#respond Thu, 07 Aug 2014 12:49:06 +0000 https://nerdranchighq.wpengine.com/blog/locking-in-swift-helping-protect-me-from-myself/ Swift helps you write code with fewer bugs. But even more intriguing are the possibilities to change "things I have to worry about" in Objective-C into "things I can let the compiler worry about" in Swift.

The post Locking in Swift: Helping Protect Me from Myself appeared first on Big Nerd Ranch.

]]>

One of the things that excites me the most about Swift is the additional toolset it provides to write code with fewer bugs. This is not just pie-in-the-sky thinking: earlier today, I fixed a bug (that I had introduced) in an app written in Objective-C; that bug would not have been possible to introduce in the first place with the stronger static typing of Swift.

There are a lot of features in Swift that fall into this camp: typed arrays and dictionaries, optionals and more. But perhaps even more intriguing are the possibilities that we can build on top of those features to change “things I have to worry about” in Objective-C into “things I can let the compiler worry about” in Swift.

Let’s tackle something that’s always messy: thread safety.

The Setup

Suppose we’re writing a class that includes, among other things, an array of things (Array<T>) and a timestamp (NSDate) of when that array was last modified. For the sake of brevity, let’s limit the class to just “append an item to the array” and “get the last-modified timestamp”:

class ArrayTracker<T> {
	private var things: [T] = []
	private var lastModified: NSDate?
	// ... various other properties
	// Append an item to the array, returning a tuple of the modification
	// time we just saved and a count of the number of things in the
	// array.
	func appendToThings(item: T) -> (NSDate, Int) {
		things.append(item)
		lastModified = NSDate.date()
		return (lastModified, things.count)
	}
	// Get the timestamp of when last modified the array.
	// Returns nil if the array has never been modified.
	func lastModifiedDate() -> NSDate? {
		return lastModified
	}
	// ... various other methods
}

This covers the basic interface of our array tracker: we can append something to the array (getting back the new “last modified” time and the new number of things in the array), and we can get the “last modified” time (if there is one). You could imagine several other interesting things: get the last item in the array (doesn’t change lastModified), remove an item from the array (does change lastModified), etc.

But now here’s the catch: We want ArrayTracker to be thread-safe, and to allow multiple concurrent readers, but only one writer at a time (and a writer should get exclusive access—all readers are blocked while a writer is active). First up, we need a lock.

Step 1: The Lock

We want a readers-writer lock, which is a lock that can be acquired by multiple readers simultaneously, but can only be acquired by a single writer. There are lots of different ways to implement such a lock (on top of GCD, or using low-level atomics, or a variety of other means), but let’s not get bogged down in the details—that can be left as an exercise for you, dear reader. Instead, we’ll define a protocol that describes the interface we want our lock to satisfy. Sticking with the theme of letting the compiler do things for us, let’s avoid having lock() and unlock() methods that we have to remember to call at the right times, and instead have the lock implementation run a block that we provide:

protocol ReadWriteLock {
	// Get a shared reader lock, run the given block, and unlock
	mutating func withReadLock(block: () -> ())
	// Get an exclusive writer lock, run the given block, and unlock
	mutating func withWriteLock(block: () -> ())
}

These functions are marked as mutating because one could imagine some particular lock implementation being a struct with some internal state that needed to be modified in order to take and release locks. Assuming we have a lock implementation that satisfies this protocol (we’ll call it MyLock), what does our thread-safe version of ArrayTracker look like? Omitting things that haven’t changed:

class ArrayTracker<T> {
	// ... existing properties
	private var lock: ReadWriteLock = MyLock()
	func lastModifiedDate() -> NSDate? {
		var date: NSDate?
		// withReadLock runs the block its given synchronously, so we
		// don't need to capture self - use unowned
		lock.withReadLock { [unowned self] in
			date = self.lastModified
		}
		return date
	}
	func appendToThings(item: T) -> (NSDate, Int) {
		// we know we're going to set these before we return them, but we
		// don't have a reasonable default value; we'll use
		// implicitly-unwrapped optionals
		var date: NSDate!
		var count: Int!
		lock.withWriteLock { [unowned self] in
			self.things.append(item)
			self.lastModified = NSDate.date()
			date = self.lastModified
			count = self.things.count
		}
		return (date, count)
	}
	// ... rest of class
}

So far, so good. Now our two methods (plus many more that have been elided—you’re still keeping them in mind, right?) are thread-safe. However, the implementations look a little messy: in both, we have to create local variables, assign to them from inside the block, then return them. There’s got to be a better way.

Step 2: Cleanup via Generics

Everything we’ve done so far could have been done almost exactly the same way in Objective-C (aside from items being a generic array), but now let’s move on to something we can’t do. Instead of having to capture values within the “lock blocks,” what if we give those blocks the ability to return arbitrarily-typed things? Let’s modify our lock protocol:

protocol ReadWriteLock {
	// Get a shared reader lock, run the given block, unlock, and return
	// whatever the block returned
	mutating func withReadLock<T>(block: () -> T) -> T
	// Get an exclusive writer lock, run the given block, unlock, and
	// return whatever the block returned
	mutating func withWriteLock<T>(block: () -> T) -> T
}

Now we can clean up our class:

func lastModifiedDate() -> NSDate? {
	// return the result of the call to withReadLock...
	return lock.withReadLock { [unowned self] in
		// ... which is the date that we want
		return self.lastModified
	}
}
func appendToThings(item: T) -> (NSDate, Int) {
	return lock.withWriteLock { [unowned self] in
		self.things.append(item)
		self.lastModified = NSDate.date()
		return (self.lastModified!, self.things.count)
	}
}

Much better! We no longer have to declare local variables before the “lock blocks,” set them inside and then return them.

Interlude: The Real Problem

Now we have a nice, clean way of protecting access to data behind a lock. That’s great—there’s a lot to be said for readable code, and our thread-safe versions of these methods are only two lines longer than the original, unsafe versions (and one of those lines is just an extra closing brace). However, let’s go back to what we really want to accomplish: how can we get the compiler to enforce things that, in Objective-C, we typically have to reason out ourselves? Having pretty locking mechanisms are great, but we still have to worry about the locking. We have to make sure we never access things or lastModified outside of a lock. If the class is big or has a lot of other moving parts, that can become difficult to keep track of. What we really want is to get the compiler to enforce that we only access those data while we are holding the lock.

Step 3: The Protector

We want to make lastModified and things impossible to access without locking, which will require moving them out of ArrayTracker and into something else. Let’s define that something else:

// Protector holds onto an item of type T, and only allows access to it
// from within a "lock block"
class Protector<T> {
	private var lock: ReadWriteLock = MyLock()
	private var item: T
	// initialize an instance with an item
	init(_ item: T) {
		self.item = item
	}
	// give read access to "item" to the given block, returning whatever
	// that block returns
	func withReadLock<U>(block: (T) -> U) -> U {
		return lock.withReadLock { [unowned self] in
			return block(self.item)
		}
	}
	// give write access to "item" to the given block, returning whatever
	// that block returns
	func withWriteLock<U>(block: (inout T) -> U) -> U {
		return lock.withWriteLock { [unowned self] in
			return block(&self.item)
		}
	}
}

Whew! Let’s unpack the signature of withReadLock:

  • withReadLock<U> is a generic method inside of a generic class. That means there are two generic types involved: T from our class, and U from this method.
  • (block: (T) -> U) means withReadLock takes as its sole parameter a block that takes a T (in particular, our protected item) and returns anything at all.
  • -> U says that the return type is U; that is, we return whatever the block returns. This is the same trick we used in Step 2 above, to get our lock protocol to return whatever the block we give it returns.

withWriteLock is the almost the same, with the difference that the argument to block is inout, meaning the block is allowed to modify it.

Step 4: Tying It All Together

Time to replace lastModified and things with a protected version:

// Let's define a struct to hold our protected data. This should probably
// be embedded inside ArrayTracker, but that doesn't build in Beta 4.
private struct Protected<T> {
	var lastModified: NSDate?
	var things: [T] = []
	init() {
	}
}
class ArrayTracker<T> {
	// Put an instance of our protected data inside a Protector
	private let protector = Protector(Protected<T>())
	// ... other properties, but no longer "lock", "lastModified",
	// or "things"
	func lastModifiedDate() -> NSDate? {
		return protector.withReadLock { protected in
			return protected.lastModified
		}
	}
	func appendToThings(item: T) -> (NSDate, Int) {
		return protector.withWriteLock { protected in
			protected.things.append(item)
			protected.lastModified = NSDate.date()
			return (protected.lastModified!, protected.things.count)
		}
	}
	// ... rest of class
}

Now we’ve freed up some mental space! It’s no longer possible for us to accidentally access lastModified or things without obtaining the lock, so we don’t have to think about it at all. Not only that, but because Protected is a struct (and therefore has value semantics), it’s now a compile-time error to try to modify the protected item inside of a read lock:

	// WRONG: Incorrectly try to append to things with just a read lock
	func appendToThingsWithReadLock(item: T) {
		protector.withReadLock { protected -> () in
			// This line fails to compile:
			// "Immutable value of type [T] only has mutating members
			// named 'append'"
			protected.things.append(item)
			// This line refuses to compile too:
			// "Cannot assign to 'lastModified' in 'protected'"
			protected.lastModified = NSDate.date()
		}
	}

The protection against incorrectly using a read lock and modifying the protected value isn’t perfect. If Protected were a class instead of a struct, the last example would build without complaint (but be thread-unsafe). If Protected were a struct but contained properties that were instances of classes (i.e., reference types), we could call methods on those properties that might modify them (which would also be thread-unsafe). However, we’ve at least significantly reduced the things we have to manage ourselves.

Parting Thoughts

One of the most difficult problems we have as software developers is managing complexity, and Swift gives us a lot of tools to lean on the compiler for help. In this example, we could have followed most of the same steps in Objective-C, but without generics, the most natural way of writing the Protector class would have tied it tightly to ArrayTracker.

Swift, much more than Objective-C, is going to reward careful, considered API design. It’s exciting to be on the ground floor: making full use of the language is going to require unlearning some patterns we’re used to, and figuring out idiomatic replacements is going to take some time and creativity.

The post Locking in Swift: Helping Protect Me from Myself appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/locking-in-swift-helping-protect-me-from-myself/feed/ 0
Make iOS 7's Dynamic Type Easier to Use with our Open-Source BNRDynamicTypeManager https://bignerdranch.com/blog/make-ios-7s-dynamic-type-easier-to-use-with-our-open-source-bnrdynamictypemanager/ https://bignerdranch.com/blog/make-ios-7s-dynamic-type-easier-to-use-with-our-open-source-bnrdynamictypemanager/#respond Thu, 06 Feb 2014 16:49:06 +0000 https://nerdranchighq.wpengine.com/blog/make-ios-7s-dynamic-type-easier-to-use-with-our-open-source-bnrdynamictypemanager/ Using iOS 7's Dynamic Type adds polish to an app. Unfortunately, it can feel cumbersome to use. BNRDynamicTypeManager is an open-source library that makes it less painful.

The post Make iOS 7's Dynamic Type Easier to Use with our Open-Source BNRDynamicTypeManager appeared first on Big Nerd Ranch.

]]>

With iOS 7, Apple introduced a system called Dynamic Type that allows applications to use fonts that vary in size based on the device’s configuration. Taking advantage of Dynamic Type adds polish to an app, but unfortunately, it can feel cumbersome to use. BNRDynamicTypeManager is an open-source library that makes it less painful.

Typical Dynamic Type Support

At first glance, using Dynamic Type seems very simple: choose one of the new text styles as your font in Interface Builder, or use +[UIFont preferredFontForTextStyle:] in code, and then when you run your app, you get a dynamically sized font!

Everything looks great… until you switch to the device preferences and change the font size while your app is running. Switching back to your app, you’ll find that it still shows the old size. And if you dig into the documentation, and you’ll find that Apple mentions that it’s “important to observe the UIContentSizeCategoryDidChangeNotification so that you can re–lay out the text when the user changes the content size category.”

In practice, this means you probably end up modifying the view controller(s) that contain labels or buttons or other elements that use the new text styles, have them add themselves as observers for UIContentSizeCategoryDidChangeNotification, and you have a little helper method that reassigns the fonts of all the labels, such as:

    - (void)didReceiveUIContentSizeCategoryDidChangeNotification:(NSNotification *)note
    {
        self.bodyLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
        self.headlineLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
        self.fooButton.titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
        // etc.
    }

After writing that a few times, and especially if writing that involved making IBOutlets for labels that you otherwise don’t need in your view controller, you probably start thinking that there has to be a better way.

Well, we made one: the BNRDynamicTypeManager library. Here’s how to use it.

Using the BNRDynamicTypeManager library

Installing BNRDynamicTypeManager

If you’re already using Cocoapods, add BNRDynamicTypeManager to your Podfile:

    pod 'BNRDynamicTypeManager', '~> 0.1.0'

If you’re not, grab the relevant files out of the GitHub repository and add them to your project.

Using BNRDynamicTypeManager with Interface Builder

In the simplest case, if you just want to have labels, buttons, text fields or text views that have a text style-based font, change the class of the element in the XIB to the appropriate BNRDynamicTypeManager subclass (e.g., UILabel becomes BNRDynamicTypeManagedLabel – see the README for the full list).

That’s it! Now that element will use the appropriate font on startup, and its font will be updated for you whenever the app receives UIContentSizeCategoryDidChangeNotification.

Using BNRDynamicTypeManager Programatically

If you create UI elements programatically, or can’t change your XIB classes for some other reason (perhaps you’ve already subclassed UILabel for your own purposes), you can explicitly tell BNRDynamicTypeManager to watch elements for you. For the common UIKit classes (UILabel, UIButton, UITextField, UITextView), there are methods provided:

    [[BNRDynamicTypeManager sharedInstance] watchLabel:label
                                             textStyle:UIFontTextStyleBody];
    [[BNRDynamicTypeManager sharedInstance] watchButton:button
                                              textStyle:UIFontTextStyleBody];
    // etc.

If you want to watch an element that isn’t a subclass of the supported classes but has a UIFont *font reachable via a keypath, you can use -[BNRDynamicTypeManager watchElement:fontKeypath:textStyle:]. There is no associated “unwatch” method. BNRDynamicTypeManager doesn’t keep strong references to the views it’s watching, so it doesn’t interfere with their normal lifecycles.

Under the Hood

Internally, BNRDynamicTypeManager is mostly a thin wrapper around NSMapTable that adds itself as an observer for UIContentSizeDidChangeNotification. NSMapTable is what allows us to hold weak references to the elements we’re watching while still keeping track of the text style to use (and the keypath to the object’s font). The BNRDynamicTypeManager class itself isn’t very big; if you’re curious, I’d recommend having a look.

The post Make iOS 7's Dynamic Type Easier to Use with our Open-Source BNRDynamicTypeManager appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/make-ios-7s-dynamic-type-easier-to-use-with-our-open-source-bnrdynamictypemanager/feed/ 0
Should I Use a Property or an Instance Variable? https://bignerdranch.com/blog/should-i-use-a-property-or-an-instance-variable/ https://bignerdranch.com/blog/should-i-use-a-property-or-an-instance-variable/#respond Mon, 14 Oct 2013 21:21:20 +0000 https://nerdranchighq.wpengine.com/blog/should-i-use-a-property-or-an-instance-variable/

One of our interns recently asked what seemed like a fairly innocuous question: “What are the scenarios where the use of instance variables would be more preferred than using properties?” At Big Nerd Ranch, we strongly prefer using properties to direct ivar access, but take a sampling of the Objective-C community and you’ll find almost as many answers as there are developers:

The post Should I Use a Property or an Instance Variable? appeared first on Big Nerd Ranch.

]]>

One of our interns recently asked what seemed like a fairly innocuous question: “What are the scenarios where the use of instance variables would be more preferred than using properties?” At Big Nerd Ranch, we strongly prefer using properties to direct ivar access, but take a sampling of the Objective-C community and you’ll find almost as many answers as there are developers:

  • “I always use properties and always access via properties (except when ivar access is strictly required).”

  • “I strongly prefer properties unless there is a very good reason to use ivars.”

  • “I generally prefer properties but will occasionally use ivars for simple state variables where the overhead of a property is unnecessary.”

  • “I use properties for ‘public’ things and ivars for ‘private’ things.”

  • “I often change ‘self.value’ calls to ‘_value’ because it’s much faster.”

Using properties has some very tangible benefits, particularly when it comes to debugging: they provide a single place to set a breakpoint on access or change, they can be overridden to add logging or other functionality, etc. Many of the answers that give some preference for ivars express concerns about the performance overhead of properties. We believe that the overhead is insignificant for most applications, but thought it would be fun to prove it. Is it possible to create a pathological app where you can actually see the difference? (Yes.) Just how much overhead is there in the message send used for setting and getting a property? (Hint: it’s measured in nanoseconds.)

Testing the Performance Overhead of Properties

Screenshot of Property Performance projectPropertyPerformance project on Github

We don’t need to get very elaborate to come up with a test that stresses properties. The app performs a meaningless but expensive computation and calls setNeedsDisplay as quickly as possible, up to the limit of how quickly iOS will redraw the display (120 frames per second). The computation is a for loop that performs floating point additions, so we can easily scale how expensive it is by adjusting the number of iterations in the for loop.

To start, let’s investigate two different loops: one that accesses a property via the default getter and one that accesses an ivar directly.

// Inner loop using properties.
for (NSUInteger i = 0; i < loopSize; i++) {
    x += self.propertyValue;
}
// Inner loop using instance variables.
for (NSUInteger i = 0; i < loopSize; i++) {
    x += _ivarValue;
}

The following chart plots the performance of the app in frames per second on an iPhone 5:

Graph of FPS for properties versus instance variablesAt large loop sizes, the property version of the for loop is about five times slower than the ivar version in iOS 6 and about 3.5 times slower in iOS 7. Let’s look at the disassembly to see why. First, the property version:

; Inner loop using properties.
0xc4a7a:  mov    r0, r11         ; move address of "self" into r0
0xc4a7c:  mov    r1, r6          ; move getter method name into r1
0xc4a7e:  blx    0xc6fc0         ; call objc_msgSend(self, getterName)
0xc4a82:  vmov   d16, r0, r0     ; move the result of the getter into d16
0xc4a86:  subs   r5, #1          ; subtract 1 from our loop counter
0xc4a88:  vadd.f32 d9, d9, d16   ; add d9 and d16 and store result into d16
0xc4a8c:  bne    0xc4a7a         ; repeat unless our loop counter is now 0

Every time through the loop, we have to call objc_msgSend to call the property’s getter, move that result into the d16 register, and then perform the actual addition via vadd.f32, a NEON instruction (more on that later). This disassembly did not change between the runs on iOS 6 and iOS 7, strongly suggesting that Apple sped up objc_msgSend, which by all accounts was already quite fast. This also lends even more weight to the performance mantra of “profile, don’t guess”—performance issues can change dramatically between different pieces of hardware (probably obvious) and between iOS updates even on the same hardware (perhaps less obvious).

On to the ivar version:

; Inner loop using instance variables.
0xc4aba:  vadd.f32 d9, d9, d0    ; add d9 and d0 and store result into d9
0xc4abe:  subs   r0, #1          ; subtract 1 from our loop counter
0xc4ac0:  bne    0xc4aba         ; repeat unless our loop counter is now 0

In the ivar case, the compiler loads the current value of the ivar into the d0 register before entering the loop. It might come as a surprise that the compiler makes the assumption that the value of the ivar will not change while the loop is running. The short answer is that because the ivar is not declared as volatile, the compiler is free to “assume its value cannot be modified in unexpected ways”; for more details, see Compiler Optimization and the volatile Keyword at the ARM website.

Overhead of objc_msgSend

This pathological app is a lot of fun to play with, but it isn’t particularly accurate, since it’s tied to redrawing the display and all the myriad things associated with that. If we abandon the UI and focus on timing properties, we can get some pretty good information. As with any timing information, there are a lot of caveats: measurements were taken on an iPhone 5 running iOS 7.0, and we were reading a single 32-bit float that was already in the L1 cache. Under these circumstances, reading directly via the ivar takes about 3 nanoseconds, and reading via the getter takes about 11 nanoseconds.

Practical Considerations

Are there apps that need to be concerned with performance differences measured in single-digit nanoseconds? Sure. But do most? No way. Consider this method call, which everyone will recognize:

[self.navigationController pushViewController:self.someOtherViewController animated:YES];

Is it slower to use self.someOtherViewController instead of _someOtherViewController? Yeah, by about 8 nanoseconds. Using some tricks Mark D talked about with DTrace, we can provide some context. On iOS 7.0, that single line of code results in more than 800 calls to objc_msgSend, and by the time the other view controller has actually appeared on screen, the count jumps to more than 150,000. Think about that: counting objc_msgSend time alone, that single property access costs 1/800th of what it costs just to kick off the presentation of the view controller. It’s practically free, and because of that, the consistency of the code offered by using property accessors wins out. It’s time to stop worrying and embrace properties.

Dipping a Toe into NEON

Because it’s not really germane to the original question, we haven’t talked much about the actual addition happening in the inner loops of the sample app. The assembly instruction vadd.f32 is a NEON instruction. It takes two 64-bit NEON registers, treats them as each holding two 32-bit floating point numbers (in their top and bottom 32 bits), and performs two 32-bit additions simultaneously, storying the two 32-bit results into the top and bottom halves of a third 64-bit NEON register. However, in all the compiler-generated loops we’ve seen so far, we’re only performing a single addition in each iteration of the for loop—the other half of the vadd.f32 operation is being discarded at the end of the loop, so we’re wasting half our potential performance!

We can write a NEON-enabled version of the loop using the functions found in the arm_neon.h header. Be warned that blindly including this file and using the functions it declares will leave your app unable to run on both the simulator and older hardware that doesn’t support the NEON instruction set. Here is a version of the for loop that uses 64-bit NEON intrinsics:

{
    // A float32x2_t corresponds to a 64-bit NEON register we treat as having
    // two 32-bit floats in each half. vmov_n_f32() initializes both halves
    // to the same value - 0.0f, in this case.
    float32x2_t x_pair = vmov_n_f32(0.0f);
    // Note that we now increment by 2 since we're doing two adds on each pass.
    for (NSUInteger i = 0; i < loopSize; i += 2) {
        // Construct a pair of 32-bit floats, both initialized to our ivar.
        float32x2_t pair = vmov_n_f32(_value);
        // Perform vadd.f32
        x_pair = vadd_f32(x_pair, pair);
    }
    // To get our final result, we need to extract both halves, or lanes, of
    // our accumulator, and add them together, storing the result in the
    // CGFloat x.
    x = vget_lane_f32(x_pair, 0) + vget_lane_f32(x_pair, 1);
}

But we don’t have to stop there—the NEON instructions also allow us to use 128-bit wide registers that hold four 32-bit floats each; see NEON registers for how all these different levels of registers are overlaid on top of each other. Here is a version of the for loop that uses 128-bit NEON intrinsics. The strategy and logic is the same as the 64-bit version, except we get four floats per variable instead of two:

{
    float32x4_t x_quad = vmovq_n_f32(0.0f);
    for (NSUInteger i = 0; i < loopSize; i += 4) {
        float32x4_t quad = vmovq_n_f32(_property);
        x_quad = vaddq_f32(x_quad, quad);
    }
    x  = vgetq_lane_f32(x_quad, 0);
    x += vgetq_lane_f32(x_quad, 1);
    x += vgetq_lane_f32(x_quad, 2);
    x += vgetq_lane_f32(x_quad, 3);
}

How do these NEON versions compare? The 64-bit NEON version is about twice as fast as the normal ivar version, and the 128-bit NEON version is about four times as fast as the normal ivar version:

Graph of FPS for properties versus instance variables

There is a tremendous amount of power tucked away in NEON. Most applications will never need it, but it’s nice to know it’s there.

The post Should I Use a Property or an Instance Variable? appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/should-i-use-a-property-or-an-instance-variable/feed/ 0