Static items

Syntax
StaticItem :
   ItemSafety?1 static mut? IDENTIFIER : Type ( = Expression )? ;

1

The safe and unsafe function qualifiers are only allowed semantically within extern blocks.

A static item is similar to a constant, except that it represents a precise memory location in the program. All references to the static refer to the same memory location.

Static items have the static lifetime, which outlives all other lifetimes in a Rust program. Static items do not call drop at the end of the program.

The static declaration defines a static value in the value namespace of the module or block where it is located.

The static initializer is a constant expression evaluated at compile time. Static initializers may refer to other statics.

Non-mut static items that contain a type that is not interior mutable may be placed in read-only memory.

All access to a static is safe, but there are a number of restrictions on statics:

  • The type must have the Sync trait bound to allow thread-safe access.

The initializer expression must be omitted in an external block, and must be provided for free static items.

The safe and unsafe qualifiers are semantically only allowed when used in an external block.

Statics & generics

A static item defined in a generic scope (for example in a blanket or default implementation) will result in exactly one static item being defined, as if the static definition was pulled out of the current scope into the module. There will not be one item per monomorphization.

This code:

use std::sync::atomic::{AtomicUsize, Ordering};

trait Tr {
    fn default_impl() {
        static COUNTER: AtomicUsize = AtomicUsize::new(0);
        println!("default_impl: counter was {}", COUNTER.fetch_add(1, Ordering::Relaxed));
    }

    fn blanket_impl();
}

struct Ty1 {}
struct Ty2 {}

impl<T> Tr for T {
    fn blanket_impl() {
        static COUNTER: AtomicUsize = AtomicUsize::new(0);
        println!("blanket_impl: counter was {}", COUNTER.fetch_add(1, Ordering::Relaxed));
    }
}

fn main() {
    <Ty1 as Tr>::default_impl();
    <Ty2 as Tr>::default_impl();
    <Ty1 as Tr>::blanket_impl();
    <Ty2 as Tr>::blanket_impl();
}

prints

default_impl: counter was 0
default_impl: counter was 1
blanket_impl: counter was 0
blanket_impl: counter was 1

Mutable statics

If a static item is declared with the mut keyword, then it is allowed to be modified by the program. One of Rust’s goals is to make concurrency bugs hard to run into, and this is obviously a very large source of race conditions or other bugs

For this reason, an unsafe block is required when either reading or writing a mutable static variable. Care should be taken to ensure that modifications to a mutable static are safe with respect to other threads running in the same process.

Mutable statics are still very useful, however. They can be used with C libraries and can also be bound from C libraries in an extern block.

#![allow(unused)]
fn main() {
fn atomic_add(_: *mut u32, _: u32) -> u32 { 2 }

static mut LEVELS: u32 = 0;

// This violates the idea of no shared state, and this doesn't internally
// protect against races, so this function is `unsafe`
unsafe fn bump_levels_unsafe() -> u32 {
    unsafe {
        let ret = LEVELS;
        LEVELS += 1;
        return ret;
    }
}

// As an alternative to `bump_levels_unsafe`, this function is safe, assuming
// that we have an atomic_add function which returns the old value. This
// function is safe only if no other code accesses the static in a non-atomic
// fashion. If such accesses are possible (such as in `bump_levels_unsafe`),
// then this would need to be `unsafe` to indicate to the caller that they
// must still guard against concurrent access.
fn bump_levels_safe() -> u32 {
    unsafe {
        return atomic_add(&raw mut LEVELS, 1);
    }
}
}

Mutable statics have the same restrictions as normal statics, except that the type does not have to implement the Sync trait.

Using Statics or Consts

It can be confusing whether or not you should use a constant item or a static item. Constants should, in general, be preferred over statics unless one of the following are true:

  • Large amounts of data are being stored.
  • The single-address property of statics is required.
  • Interior mutability is required.