More commentary:

> Another feature where a pragma seems out of place is inlining, although in this case I can’t really blame Rust for it. [...] I’ve always thought it should be up to the caller to say which functions they’d like inlined, and the function should just mind its own business.

It should actually be up to the compiler to make that call. And for all functions within a crate it is up to the compiler. Every function (that's not marked as `#[inline(never)]`) is a candidate for inlining anywhere else it's called within the same crate. The #[inline] attribute is a hint to the compiler that it might want to inline it more often, but the compiler is free to decide not to inline it.

Where #[inline] really matters is when calling it from other crates. The compiler can only inline functions that it actually can see the definition of. This should be obvious from the fact that inlining requires the compiler to copy the definition to the point where it was called. So what the #[inline] attribute does, besides hint to the compiler that you might like to inline it, is include the AST for the function in the library. This is actually the same thing it already does for generic functions (since generic functions need to be instantiated by the caller for arbitrary type parameters). So a generic function doesn't actually need #[inline] at all to be inlined. But non-generic functions do.

> (but five function calls (from_ptr, from_utf8, to_bytes, unwrap, to_string) just to convert a C string to a Rust string seems like an excessive amount of ceremony

You're right. The std::ffi module is relatively new. There used to be a simpler way to convert from a * const u8 into a &str but it appears to have fallen by the wayside.

> Rust won’t read C header files

You should check out https://github.com/crabtw/rust-bindgen. It's a tool that can generate the Rust extern interfaces from C headers.

> A function that does something unsafe (e.g. pointer arithmetic or calling C libraries) can be exported as safe.

If this weren't true than large swaths of the standard library would be unsafe, including anything that calls into libc or makes system calls.

When you expose a safe interface that uses `unsafe`, you're promising that you will guarantee safety yourself in a way the compiler cannot verify. If you do not want to (or cannot) guarantee that your interface is safe, then you should mark it as `unsafe` and let the caller deal with it. But the ability to wrap `unsafe` code in safe interfaces is absolutely crucial for lots of things, including much of the standard library. The hope is that most people writing Rust code won't ever have to use `unsafe` themselves because the standard library (or other people's libraries) will provide the necessary safe interfaces. But people writing safe interfaces around unsafe constructs need the ability to do this.

> but until a few days ago, Cargo didn’t understand linker flags

I believe this was already addressed in another comment, but this is not actually true. Cargo build scripts can provide flags to give to rustc, and `extern` declarations can be annotated with attributes specifying certain linker flags (such as the name of the library to link to).