Skip to main content

ctor

Attribute Macro ctor 

Source
#[ctor]
Expand description

Marks a function or static variable as a library/executable constructor. This uses OS-specific linker sections to call a specific function at load time.

§Important notes

Rust does not make any guarantees about stdlib support for life-before or life-after main. This means that the ctor crate may not work as expected in some cases, such as when used in an async runtime or making use of stdlib services.

Multiple startup functions/statics are supported, but the invocation order is not guaranteed.

The ctor crate assumes it is available as a direct dependency, If you re-export ctor items as part of your crate, you can use the crate_path parameter to redirect the macro’s output to the correct crate, or use the declarative::ctor form.

§Examples

Print a startup message (using libc_print for safety):

use libc_print::std_name::println;

#[ctor(unsafe)]
fn foo() {
  // Using libc_print which is safe in `#[ctor]`
  println!("Hello, world!");
}

println!("main()");

Make changes to static variables:

use ctor::*;
use std::sync::atomic::{AtomicBool, Ordering};
static INITED: AtomicBool = AtomicBool::new(false);

#[ctor(unsafe)]
fn set_inited() {
  INITED.store(true, Ordering::SeqCst);
}

Initialize a HashMap at startup time:

#[ctor(unsafe)]
pub static STATIC_CTOR: HashMap<u32, String> = {
  let mut m = HashMap::new();
  for i in 0..100 {
    m.insert(i, format!("x*100={}", i*100));
  }
  m
};

§Details

The #[ctor] macro makes use of linker sections to ensure that a function is run at startup time.

#[ctor(unsafe)]
fn my_init_fn() {
  /* ... */
}

The above example translates into the following Rust code (approximately):

#[used]
#[cfg_attr(target_os = "linux", link_section = ".init_array")]
#[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func,mod_init_funcs")]
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
/* ... other platforms elided ... */
static INIT_FN: extern fn() = {
    extern fn init_fn() { my_init_fn(); };
    init_fn
};

For static items, the macro generates a std::sync::OnceLock that is initialized at startup time. #[ctor] on static items requires the default std feature.

#[ctor]
static FOO: HashMap<u32, String> = unsafe {
  let mut m = HashMap::new();
  for i in 0..100 {
    m.insert(i, format!("x*100={}", i*100));
  }
  m
};

The above example translates into the following Rust code (approximately), which eagerly initializes the HashMap inside a OnceLock at startup time:

static FOO: FooStatic = FooStatic { value: ::std::sync::OnceLock::new() };
struct FooStatic {
  value: ::std::sync::OnceLock<HashMap<u32, String>>,
}

impl ::core::ops::Deref for FooStatic {
  type Target = HashMap<u32, String>;
  fn deref(&self) -> &Self::Target {
    self.value.get_or_init(|| unsafe {
      let mut m = HashMap::new();
      for i in 0..100 {
        m.insert(i, format!("x*100={}", i*100));
      }
      m
    })
  }
}

#[ctor]
unsafe fn init_foo_ctor() {
  _ = &*FOO;
}