Rust doesn’t guarantee a stable ABI. You can link Rust’s libstd dynamically, but the library will be very version-specific. You can get away with this if only you distribute it and libraries that depend on it all together.
You can make one dynamic C ABI library that consists of multiple static Rust libraries. LTO will dedupe them.
You can make one dynamic C ABI library that consists of multiple static Rust libraries. LTO will dedupe them.
Right, but if two of your downstream dependencies both write a .so file in rust, won’t it all break down as you have two copies of the std allocator, one in each .so file?
No. It’s no different than having Rust’s allocator (which may not be your system’s allocator) and C’s allocator. When you cross ffi boundaries, you generally try not to assume any particular allocator. It’s up to each library to allocate memory internally and expose their own free routines. The same goes for Rust when exposing a C interface.
Rust doesn’t have a runtime, in the sense that Python or Go have a runtime, but it does have a standard library that gets statically linked into the resulting binary.
As long as these aren’t publicly exposed (so are defined as static in C terms) it is fine to go. Also you can always use no_core and do not use any of the Rust stdlib in the library.
If you are going to do this, you might want to define you exposed API surface area with a common naming convention (my_api_* or something), then use a linker version script to ensure that only the symbols you expect to export are actually exported.
I do this with C++ libraries. I’ll statically link libstdc++ into libwhatever.so, then hide all of the stdc++ symbols, libgcc symbols, etc. My libraries usually only have dynamic dependencies on libc. I have no idea if you can replicate this pattern in rust, I haven’t tried yet, but doing this has been tremendously helpful for deploying libraries in the environment I am deploying them too.
The obvious option is to dynamically link the rust standard library as well, rustc test.rs --crate-type=dylib -C prefer-dynamic (or similarly passing those options through cargo). Or to use no_std.
I don’t know if there is a more elegant way though.
No it does not, because you cannot represent generics in C’s ABI. You can probably compile the standard lib as a Rust dylib, but then you don’t get a stable ABI and it’s of limited use.
Anyone know if it is possible to make multiple dynamically linked rust libs that are usable from C without including the rust runtime multiple times?
Rust doesn’t guarantee a stable ABI. You can link Rust’s libstd dynamically, but the library will be very version-specific. You can get away with this if only you distribute it and libraries that depend on it all together.
You can make one dynamic C ABI library that consists of multiple static Rust libraries. LTO will dedupe them.
Right, but if two of your downstream dependencies both write a .so file in rust, won’t it all break down as you have two copies of the std allocator, one in each .so file?
No. It’s no different than having Rust’s allocator (which may not be your system’s allocator) and C’s allocator. When you cross ffi boundaries, you generally try not to assume any particular allocator. It’s up to each library to allocate memory internally and expose their own free routines. The same goes for Rust when exposing a C interface.
Rust doesn’t have a runtime, in the sense that Python or Go have a runtime, but it does have a standard library that gets statically linked into the resulting binary.
That is a runtime, I’m confused how this can work without causing multiple redefinitions of the stdlib symbols.
As long as these aren’t publicly exposed (so are defined as
static
in C terms) it is fine to go. Also you can always useno_core
and do not use any of the Rust stdlib in the library.If you are going to do this, you might want to define you exposed API surface area with a common naming convention (my_api_* or something), then use a linker version script to ensure that only the symbols you expect to export are actually exported.
This looks possible in rust: https://users.rust-lang.org/t/linker-version-script/26691 For information about the linker scripts: https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_25.html
I do this with C++ libraries. I’ll statically link libstdc++ into libwhatever.so, then hide all of the stdc++ symbols, libgcc symbols, etc. My libraries usually only have dynamic dependencies on libc. I have no idea if you can replicate this pattern in rust, I haven’t tried yet, but doing this has been tremendously helpful for deploying libraries in the environment I am deploying them too.
The obvious option is to dynamically link the rust standard library as well,
rustc test.rs --crate-type=dylib -C prefer-dynamic
(or similarly passing those options through cargo). Or to useno_std
.I don’t know if there is a more elegant way though.
Does that work in the presence of generics?
It should, though it’s probably duplicating the asm versions of the monomorphized methods in both libs.
No it does not, because you cannot represent generics in C’s ABI. You can probably compile the standard lib as a Rust dylib, but then you don’t get a stable ABI and it’s of limited use.