16 comments

[ 3.2 ms ] story [ 49.2 ms ] thread
Very impressive! This post demonstrates a great deal of understanding of the semantics of both languages, and the resultant interface looks both usable and thorough. I especially enjoyed the use of a Rust macro to emulate the syntax of Objective-C message passing, as well as the demonstration of the utility of "phantom" type parameters.
That's CoreFoundation, not Foundation. CoreFoundation is plain C and relatively small in feature set. Foundation is Objective-C and much larger.

It's a lot easier to work with the Foundation Objective-C API. They both manipulate the same data structures (or nearly the same with zero-cost bridging) but Foundation is far less verbose and a little more dynamic (no need for manual buffer allocations and configuring callback structures).

Really impressive stuff.
Great writeup. More interested though on how one can use Rust to write iOS, OS X and Android framework/library. This will make Rust replacing C++ for cross platform codes.
Only if Rust provides something comparable with Qt, SDL, Cocos-2D, openFrameworks....

Otherwise having platform FFI pain, C and C++ interop pain is just too much.

Strictly speaking, [string UTF8String] is equivalent to '((const char <star>(<star>)(id, SEL))objc_msgSend)(string, selector)'. Since objc_msgSend is vararg function, it will promote it's arguments, so for example method taking float will be called with double. Casting to a proper function type works around that.
Oh good point, I hadn't considered vararg promotion!

Do you know if casting to a function also make objc_msgSend_stret and objc_msgSend_fpret unnecessary? I haven't yet worked with any methods that return structs or floating point values, so I haven't had to deal with them yet.

Edit: Oh, nope, my bad, objc_msgSend_stret is the one where you definitely have to cast: http://blog.lazerwalker.com/blog/2013/10/12/the-objective-c-...

As you discovered, _stret/_fpret remain necessary.

objc_msgSend()'s type is not expressible in C; the only time it is correct to call objc_msgSend without a cast is when calling a method IMP with the literal vararg type of id (*method) (id self, SEL sel, ...);

In other words, essentially never.

Just to bring up something I've been thinking about, Rust would have a lower impedance mismatch with Swift interfaces - memory safe APIs, with a more C-like method syntax, more strongly typed in general (optionals, generics...), more functional (although AFAIK there aren't currently many Swift-specific APIs, as opposed to Objective-C ports), and so on. Its binary module system also matches Rust somewhat better than Objective-C's basically #include based system. Unfortunately, Swift's runtime, aside from being more complicated than Objective-C's, is completely undocumented (and closed-source), so it would take a good bit of reverse engineering to create a bridge.
You can do this sort of thing with Objective C precisely because the layer between ObjC and C is so clean and concise. It's a strict superset of the C language; the C at its core is pure and entirely unmolested.

What makes interoperability possible is Rust's existing C interop.

Rust doesn't have any Swift interop.

That's interesting. I always had problems using objc_msgSend via FFIs because if I remember correctly it was implemented via setjmp and longjmp.
Huh, Obj-C got its stairway to heaven :)
Very cool – and oddly relevant to me since as well as taking an interest in Rust recently, I'm also writing an Objective-C bridge for Julia (which I'll write about soon).

Like the languages themselves the bridges are polar opposites, of course – static and safe vs. flexible and interactive, etc. It's great to see it (and learn from it) being done on the other side of those tradeoffs.