Uses for #[no_mangle] with Rust ABI?


#1

Are there significant use-cases for adding #[no_mangle] to Rust functions?

#[no_mangle]
pub fn foo() {}

I’m guessing that — perhaps outside internals of the the compiler or std — there are no good uses for it, since Rust ABI is not stable.

I wonder if rustc could add a lint that warns about forgotten extern in such case?


#2

The main use case for me would be when compiling to asm - the function name is a lot nicer when unmangled. See my rustc-optimization repository which uses this.


#3

You can use it for type-safe dependency inversion


#4

How do you use it? Why not reexports? Isn’t that a problem it only works for public items and doesn’t support modules?


#5

Crate a:

pub struct MyStruct;

extern "Rust" {
  fn global_my() -> MyStruct;
}

Crate b:

extern crate a;

#[no_mangle]
pub extern "Rust" fn global_my() -> a::MyStruct {
  unimplemented!()
}

Strangely enough this complains about improper_ctypes, but should work.


#6

What prevents you from using pub use a::global_my instead?


#7

You can’t have cyclic extern crate dependencies.


#8

Question: why do you have 2 crates that at least conceptually have cyclic imports, rather than just merging the 2 crates?

I ask because I have a hypothesis that I’m looking to either confirm or falsify: That whenever there is a cyclic dependency in software, it’s nothing more than a strong hint that the components involved should be merged into 1, and after that perhaps re-refactored into a tree-like shape, which is a more natural form for software anyway.


#9

I don’t have anything in particular, I’m just answering OP’s question :slight_smile:

The Portability WG is considering something similar for global resource handling, https://github.com/rust-lang-nursery/portability-wg/issues/3, feel free to let those examples inform your hypothesis.


#10

I do this in ladspa.rs to deal with dynamic plugin loading (extern declaration, definition) . Maybe there’s a better way, but that’s what I came up with at the time.


#11

I think one possible use would be dealing with development tools (profilers, debuggers…) which do not have support for Rust’s name mangling scheme. Did not encounter the issue yet, though.


#12

There’s probably something I’m missing, but it seems that libloading fits your use case nicely?


#13

Not quite. The intention of the library is to enable writing plugins for LADSPA hosts in Rust. Since LADSPA is a C API, it would be nice to provide some kind of Rusty wrapper around it.

The plugin host (typically part of a DAW) dynamically loads the library and calls the LADSPA entry point. This entry point is an extern "C" function within ladspa.rs, which then defers to the user’s #[no_mangle] Rust entry point. I need some way of finding the user code so I can execute it when the LADSPA entry point is called. The user’s code cannot register itself at runtime because it would need to do so in some kind of static constructor, which we don’t have in Rust. I wanted to avoid having users implement the C entry point directly so they wouldn’t have to use unsafe code.

Sure, I could use an extern "C" ABI for the user entry point, but why should I have to when it’s Rust calling Rust?


#14

While I see and agree with your point in spirit, I do not know of any currently existing, safe, Rust-specific ABIs that can deal with dynamic linking.

With that in mind, the only real option is the C ABI I think. That said, it’s not that bad, since access through libloading is safe, in the sense that pointers into the dynamic lib are pretty much guaranteed to be valid.
In addition, you get the option of adding code written in other languages and/or by other people, as long as those languages can compile into dynamic libraries. This may not matter to you, but I’m just throwing it out there.

Other than the understandable wish to do better than the C ABI status quo, is there a specific reason for wanting a Rust ABI?


#15

The Wasm Hello World example uses #[no_mangle]:

#[no_mangle]
pub extern fn add_one(a: u32) -> u32 {
    a + 1
}

#16

But it also uses extern, so that’s the “C” (or WASM) ABI