iPhone Syncthing Client (or Rust in the Wild)

It took a bit of time, but finally fsync is available in the AppStore.

From a user perspective it’s a pretty simple iPhone client for syncthing. If you never heard about syncthing and you ever needed a self-hosted Dropbox you should give it a try. It’s very nice piece of software, it runs almost anywhere (Linux, *BSDs, Windows). It’s open source and written in Go so if you want to practice Go language - you’re welcome to contribute.

Right now fsync works in mostly “read-only” mode, i.e. you can view files, you can delete them, but there is no way to edit. This is gonna change soon.

From a technical perspective it’s probably one of the earliest iOS applications, which are partially written in Rust.

There is a lot of things to talk about and I hope to provide more technical details in the following posts, while this one covers using Rust as a language for mobile development from my experience.

How it started

Being active syncthing user I was missing opportunity to access my files from mobile and decided to write an iOS application.

Initially I’ve started writing core code in C, got a first working prototype and understood that I’m spending too much time dealing with managing memory, ownership and so on. I’ve checked available options:

  • C++
  • Rust
  • Swift
  • Objective-C

C++ was dropped immediately - I haven’t written in it since university, it changed a lot… and I wouldn’t like to invest my time into it, even if gives more advantages in short-term.

While technically it was possible to use Objective-C, it’s not good enough for low level stuff and there is a lot of low-level stuff in syncthing client.

Swift looked very promising, but it was so immature that even Apple “fanboys” were against choosing it for any serious code.

The problem I saw was that although there is an official Android client, it’s basically re-packaged syncthing. Which means it wasn’t written with mobile in mind, where being memory/traffic/battery friendly is crucial.

You get were I was heading - I’d be happy to have a cross-platform code written for mobile. And the only option in this case was Rust.

Maturity

There were moments when I hated Rust - it was a hell of a ride to the API stability. Initially it didn’t feel like a big deal (“ohh, that’s just a couple of changes here and there”). But… Little strokes fell great oaks and a couple of times I was on the edge, considering to rewrite it again in C (or C++ - yep, it’s a huge monster, but it’s also a well known + stable monster).

One of the hidden problems caused by instability was a desire to keep breaking surface as small as possible. Practically it meant ignoring existing libraries and re-inventing half-baked versions just to have a control on when and how to update it.

It was a dark time and I’m glad it’s finally over.

While language and std lib are stable now, there is still a huge necessity in a better documentation. A lot of changes have happened with no announcements and no discussions so if you were always on GitHub, monitoring issues everyday - you’re probably fine. But if you weren’t because you had to write your own code - I bet a lot of that code is already “rusty” and cries for modernization.

What was bad

At some point I was very disappointed, but I have to admit it was caused mostly by wrong expectations and naive estimations.

  1. I didn’t expect it’ll take so long to stabilize.
  2. Rust can give you exceptionally granular control of memory allocations, but it’s a manual control. You’ll pay with a longer development cycle, you’ll pay wit much more complex code and less flexibility.
  3. Rust is slow to compile and that discourages a lot, especially if you’ve got a minute to hack on an old laptop and… Boom… Half of the time is wasted in wait for compiler. More than a minute for building ~7K LOC? Yeah, I know it’s an old laptop, but that’s hardly an excuse, considering Clang can handle much bigger codebase faster.
  4. Code-management tooling (navigation/refactoring) is immature. This should improve over time with language stability, but I can’t imagine how long it will take to get something like AppCode/CLion.

As you can see only last 2 reasons are related to the language itself and I hope that at least compilation time will be addressed in upcoming 1.1 or 1.2.

What was good

Before Rust I’ve been developing in higher level languages for quite a long time. As a result, there were a lot of things I didn’t even consider - they were hidden, the choice was already made for me. The first impression after using Rust was “ohh, there are so many things I have to decide on my own now!”.

For me personally it was a refreshing experience and also a reminder that there is no way to get performance if you don’t keep it as a primary goal all the time.

Probably more than the language itself I liked the direction it takes in encouraging good engineering practices - you get built-in testing/benchmarking support and a pretty decent documentation generator. Want to go further? Mix them - and you’ll get a bullet-proof documentation, which will fail your tests if you’ve forgot to update it after an API change.

And yet there is also Cargo for getting repeatable builds out of various dependencies. Std lib was significantly reduced on the way to 1.0 due to cargo. What was not necessary or was controversial was extracted out of tree to a separate crate1.

That gut feeling of rock-solid foundation becomes even stronger after familiarizing with Rust’s development process. In short - there is a bot, which runs tests on approved pull requests and merges into master only on success. Sometimes it’s annoying, sometimes it’s slow, but you’re sure that at any point of time nothing is broken2.

iOS development

One of the most interesting questions is if there is a place for Rust in iOS development.

In most cases if you’re developing for iOS/OS X - you’d better use Objective-C and/or Swift. They provide much better integration with native frameworks and higher level to work on.

Still, Rust can shine when:

  • you need a cross-platform code
  • you target to have a very low footprint
  • you can either isolate code enough or provide automatic bindings to/from “native” code

I’d say that the last one is the real bottleneck.

A lot of iOS/OS X frameworks have a pure C interface3, 4 and therefore can be used from Rust directly5. In general it’s easy enough to access Objective C objects either6, but it’s hard to exploit that without loosing portability. So… you can’t move things down to Rust7 - you have to provide smoother interaction between “core” and “UI” by other means.

When Dropbox started cross-platform C++ development, they had the same problem and they solved it8 in Djinni - an automatic binding/proxy generator which bridges native code with cross-platform code. If someone writes a generator for Rust - it can make a huge difference9.

Another way to workaround this problem is to have a pretty isolated Rust core, which acts as a server and can be accessed through a well-known protocol. It means there will be additional costs, definitely higher than automatic data marshaling performed by Djinni. Whether it is acceptable or not depends a lot of data flow in the application.

As a conclusion - unfortunately for mobile cross-platform development Rust is pretty limited nowadays and works good only for specific use cases. A bright side though is that it’s a limitation which can be addressed by tools.

  1. crate is a synonym for a package 

  2. unless you’re out of luck and provide by yourself support for a platform which hasn’t official buildbot. 

  3. CFFoundation for example provides access to all native data structures. 

  4. Steven Sheldon got into GCD and you can even provide Rust blocks to Objective-C code. 

  5. unsafe wrappers can be generated by bindgen, it takes a bit more time to get safe wrappers though. 

  6. see examples in rust-objc, rust-objc-foundation from Steven Sheldon and rust-cocoa from Servo. 

  7. with the only exception for platform-accelerated APIs. 

  8. slides from the talk with more details about goals and motivations. 

  9. there is a chance that Dropbox writes generator for Rust as they use it internally, although I doubt it. 

Comments