#initialization #static #generics

generic_static

Generic static variables in generic functions

2 unstable releases

0.2.0 May 11, 2020
0.1.0 Jan 17, 2019

#24 in #initialization

Download history 1139/week @ 2024-06-19 1258/week @ 2024-06-26 1377/week @ 2024-07-03 1226/week @ 2024-07-10 1019/week @ 2024-07-17 1088/week @ 2024-07-24 1520/week @ 2024-07-31 889/week @ 2024-08-07 708/week @ 2024-08-14 853/week @ 2024-08-21 698/week @ 2024-08-28 895/week @ 2024-09-04 1679/week @ 2024-09-11 1180/week @ 2024-09-18 1269/week @ 2024-09-25 801/week @ 2024-10-02

5,056 downloads per month
Used in 12 crates (3 directly)

Custom license

8KB
60 lines

Problem

lets consider following code:

use once_cell::sync::OnceCell;

trait X{
    fn string() -> String;
}

// having to recompute string() over and over might be expensive (not in this example, but still)
// so we use lazy initialization
fn generic<T: X>() -> &'static str{
    static VALUE: OnceCell<String> = OnceCell::new();

    VALUE.get_or_init(||{
        T::string()
    })
}

// And now it can be used like this
struct A;
impl X for A{
    fn string() -> String{
        "A".to_string()
    }
}

struct B;
impl X for B{
    fn string() -> String{
        "B".to_string()
    }
}

fn main(){
    assert_eq!(generic::<A>(), "A");
    assert_eq!(generic::<B>(), "A"); // Wait what?
    // Not completely behaviour I was expecting
    // This is due to fact that static variable placed inside of generic function
    // wont be cloned into each version of function, but will be shared
    // Thus second call does not initialize value for B, but takes value
    // initialized in previous call.
}

Solution

This crate was designed to solve this particular problem.

Lets make some changes:

use generic_static::StaticTypeMap;
use once_cell::sync::OnceCell;

trait X{
    fn string() -> String;
}

// having to recompute string() over and over might be expensive (not in this example, but still)
// so we use lazy initialization
fn generic<T: X + 'static>() -> &'static str{ // T is bound to 'static
    static VALUE: OnceCell<StaticTypeMap<String>> = OnceCell::new();
    let map = VALUE.get_or_init(|| StaticTypeMap::new());

    map.call_once::<T, _>(||{
        T::string()
    })
}

// And now it can be used like this
struct A;
impl X for A{
    fn string() -> String{
        "A".to_string()
    }
}

struct B;
impl X for B{
    fn string() -> String{
        "B".to_string()
    }
}

fn main(){
    assert_eq!(generic::<A>(), "A");
    assert_eq!(generic::<B>(), "B");
}

Drawbacks

Current implementation uses RwLock to make it safe in concurrent applications, which will be slightly slower then regular

Dependencies

~47KB