Skip to content

ACP For OnceLock::get_unchecked and LazyLock::get_unchecked #654

@HomelikeBrick42

Description

@HomelikeBrick42

Proposal

Problem statement

Currently there is no way to get a reference to the value in a OnceLock or LazyLock without there being an atomic initialization check on every access, even if you know it has already been initialized, which adds slight overhead to every access.

Motivating examples or use cases

This is one use case where I wanted this:

use derive_more::derive::{Debug, Display};
use lasso::{Spur, ThreadedRodeo};
use rustc_hash::FxBuildHasher;
use std::sync::OnceLock;

#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)]
#[display("{}", self.as_str())]
#[debug("{:?}", self.as_str())]
pub struct InternedStr(Spur);

impl InternedStr {
    pub fn intern(s: &str) -> Self {
        InternedStr(
            INTERNER
                .get_or_init(|| ThreadedRodeo::with_hasher(FxBuildHasher))
                .get_or_intern(s),
        )
    }

    pub fn as_str(self) -> &'static str {
        // SAFETY: if `self` has been constructed, then `INTERNER` has already been initialized by `Self::intern`
        // even though this uses `unwrap_unchecked`, the compiler still needs to atomically load the
        // initialization flag inside of `get` because the atomic ordering could matter, but it does not here
        let interner = unsafe { INTERNER.get().unwrap_unchecked() };

        // SAFETY: if `self` has been constructed, then `self.0` was retrieved from `INTERNER` so it will be resolved
        unsafe { interner.try_resolve(&self.0).unwrap_unchecked() }
    }
}

static INTERNER: OnceLock<ThreadedRodeo<Spur, FxBuildHasher>> = OnceLock::new();

Solution sketch

The solution would be having OnceLock::get_unchecked/LazyLock::get_unchecked so that the atomic initialization flag doesnt have to be read at all.

They would have these signatures:

impl<T, F> LazyLock<T, F> {
    pub unsafe fn get_unchecked(this: &Self) -> &T;
    pub unsafe fn get_unchecked_mut(this: &mut Self) -> &mut T;
}
impl<T> OnceLock<T> {
    pub unsafe fn get_unchecked(&self) -> &T;
    pub unsafe fn get_unchecked_mut(&mut self) -> &mut T;
}

Alternatives

There are no other alternatives.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions