Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Officially deprecate lazy_static #214

Open
KodrAus opened this issue May 4, 2023 · 22 comments
Open

Officially deprecate lazy_static #214

KodrAus opened this issue May 4, 2023 · 22 comments

Comments

@KodrAus
Copy link
Contributor

KodrAus commented May 4, 2023

lazy_static has served the community well over the better part of a decade. Over that time, the Rust language has evolved, and better alternatives built on that evolution have emerged. That functionality has now made its way into the standard library, with aspects of it stabilizing from 1.70.0.

This library hasn't seen steady maintainership in some time. To support migration off lazy_static and towards once_cell or the standard library's types I propose we formally deprecate this library.

cc @rust-lang-nursery/libs-contributors

MarkusPettersson98 added a commit to mullvad/mullvadvpn-app that referenced this issue Jun 22, 2023
To align more with the upcoming standardizations within the Rust
ecosystem which started with the release of `1.70.0` and the inevitable
deprecation of `lazy_static`:
rust-lang-nursery/lazy-static.rs#214
MarkusPettersson98 added a commit to mullvad/mullvadvpn-app that referenced this issue Jun 22, 2023
To align more with the upcoming standardizations within the Rust
ecosystem which started with the release of `1.70.0` and the inevitable
deprecation of `lazy_static`:
rust-lang-nursery/lazy-static.rs#214
MarkusPettersson98 added a commit to mullvad/mullvadvpn-app that referenced this issue Jun 22, 2023
To align more with the upcoming standardizations within the Rust
ecosystem which started with the release of `1.70.0` and the inevitable
deprecation of `lazy_static`:
rust-lang-nursery/lazy-static.rs#214
@Fishrock123
Copy link

I think this library is still worth having, but with it's implementation switched to once_cell.

The interface of lazy_static is often much better for use cases where there isn't much worry of fallibility or misordering.

E.g. static regexes from string literals.

BlueTheDuck added a commit to BlueTheDuck/nds-rs that referenced this issue Nov 24, 2023
@frewsxcv
Copy link
Contributor

I opened a PR for documenting in the README that one can now utilize the stable std::sync::OnceLock: #216

@lordofpipes
Copy link

I think this library is still worth having, but with it's implementation switched to once_cell.

The interface of lazy_static is often much better for use cases where there isn't much worry of fallibility or misordering.

E.g. static regexes from string literals.

Are there currently any alternatives that provide a macro on top of std::cell::OnceCell and std::sync::OnceLock? It seems like LazyCell standardization is still being debated because of its usage of an extra function pointer to a closure at runtime.

@frewsxcv
Copy link
Contributor

OnceLock is simple enough in my opinion. A macro would only save you a few characters, if any.

use std::collections::HashMap;
use std::sync::OnceLock;

fn hashmap() -> &'static HashMap<u32, &'static str> {
    static HASHMAP: OnceLock<HashMap<u32, &str>> = OnceLock::new();
    HASHMAP.get_or_init(|| {
        let mut m = HashMap::new();
        m.insert(0, "foo");
        m.insert(1, "bar");
        m.insert(2, "baz");
        m
    })
}

fn main() {
    // First access to `HASHMAP` initializes it
    println!("The entry for `0` is \"{}\".", hashmap().get(&0).unwrap());

    // Any further access to `HASHMAP` just returns the computed value
    println!("The entry for `1` is \"{}\".", hashmap().get(&1).unwrap());
}

@Kimundi
Copy link
Contributor

Kimundi commented May 12, 2024

@KodrAus Sorry, another month went by too fast 😅 I just sent you the crates.io ownership invite.

@CountingBeeps
Copy link

CountingBeeps commented Jun 15, 2024

While a bit niche, this crate is commonly used in #![no_std] projects. If this is deprecated an alternative will be needed.

This usecase makes swapping to a oncelock wrapper potentially problematic.

Edit: Looking at OnceCell, it might be a suitable alternative.

@KodrAus
Copy link
Contributor Author

KodrAus commented Jun 16, 2024

@Kimundi Sorry, it looks like I managed to miss this until the invite expired 🙃 Is there any chance of resending it? I can see us going around in circles a bit here though so if I ping a random member of the nursery team with permissions to push............ @cuviper could I trouble you to cargo publish the master branch here? It's all ready to go 🙏

@cuviper
Copy link

cuviper commented Jun 21, 2024

Published!

@KodrAus
Copy link
Contributor Author

KodrAus commented Jun 22, 2024

Thanks @cuviper!

@KodrAus KodrAus pinned this issue Jun 26, 2024
@tgross35
Copy link

While a bit niche, this crate is commonly used in #![no_std] projects. If this is deprecated an alternative will be needed.

This usecase makes swapping to a oncelock wrapper potentially problematic.

Edit: Looking at OnceCell, it might be a suitable alternative.

OnceCell or LazyCell (in core::cell) are usable with no_std if they don't need to be statics. For cases where you do want a global static, spin::Lazy is now available as a no_std version of std::sync::LazyLock.


Two cents: I don't think this repo should be deprecated until OnceLock and LazyLock have been around long enough to make it into Debian / RHEL stable. It doesn't seem like a good idea to give everyone an unmaintained advisory for lazy-static and force changing to once_cell if they can't directly jump to LazyLock for MSRV reasons.

@cuviper
Copy link

cuviper commented Jul 11, 2024

RHEL 9.4 and 8.10 both have Rust 1.75, so they have OnceCell and OnceLock now. Rust 1.80 will be a while yet for RHEL to get LazyCell and LazyLock, but I think once_cell is still worthwhile in the meantime.

The "unmaintained" status here is true whether we issue an advisory or not.

@rimutaka
Copy link

rimutaka commented Aug 5, 2024

What are the alternatives for async?
This works:

// Cannot use std::cell::OnceCell because it does not support async initialization
lazy_static! {
    pub(crate) static ref CONFIG: AsyncOnce<Config> = AsyncOnce::new(async { Config::from_env().await });
}

@SimonSapin
Copy link
Contributor

tokio::sync::OnceCell supports async initialization. (The "sync" in tokio::sync refers to thread-safe synchronization like std::sync, it does not mean not-async.)

@kennytm
Copy link

kennytm commented Aug 7, 2024

The example in #214 (comment) is more like a LazyCell though.

The problems reducing usability of LazyCell-style initialization are

  1. You need #![feature(const_async_blocks)]1. (This use-side feature can be avoided if the cell is initialized with AsyncFnOnce() -> T rather than Future<Output=T>, in exchange for the definition-side #![feature(async_closure)])

  2. Unlike LazyCell which you can default the initializer to fn() -> T, there is no static type for the async block, so you need to separately define a TAIT2, making the code pretty messy like:

    #![feature(const_async_blocks, type_alias_impl_trait)]
    type ConfigFuture = impl Future<Output = Config>;
    static CONFIG: AsyncLazyCell<ConfigFuture> = AsyncLazyCell::new(async { 
        Config::from_env().await 
    });
    sample (slow) implementation of AsyncLazyCell
    use std::future::Future;
    use std::sync::Mutex;
    use tokio::sync::OnceCell;
    
    struct AsyncLazyCell<F: Future> {
        cell: OnceCell<F::Output>,
        initializer: Mutex<Option<F>>,
    }
    
    impl<F: Future> AsyncLazyCell<F> {
        pub const fn new(f: F) -> Self {
            Self {
                cell: OnceCell::const_new(),
                initializer: Mutex::new(Some(f)),
            }
        }
        
        pub async fn get(&self) -> &F::Output {
            self.cell.get_or_init(|| {
                let mut guard = self.initializer.lock().unwrap();
                let future = guard.take();
                drop(guard);
                future.expect("poisoned or something")
            }).await
        }
    }

    async_once3 works-around this by storing the future as dynamic Box<dyn Future<Output = T>>, but outside of lazy_static! I think you can only ask the user call Box::pin themselves in the static declaration, not hidden away in the const fn new().

Footnotes

  1. Tracking Issue for async {} blocks in const expressions rust-lang/rust#85368

  2. thus requiring #![feature(type_alias_impl_trait)], Tracking issue for RFC 2515, "Permit impl Trait in type aliases" rust-lang/rust#63063

  3. https://docs.rs/async_once

xoen added a commit to jnioche/carbonintensity-api that referenced this issue Oct 6, 2024
**NOTE**: This uses `LazyLock` which was stabilised recently in Rust 1.80.
This means we're effectively pushing the MSRV (minimum supported Rust
version) to 1.80+ which is relatively fresh ([July 2024]).

I'm not too keen on [the alternatives]:
1. No nothing: Create a new datetime every time the `validate_date()` is
   called
   - possibly slow/unnecesary, although maybe not a tragedy
2. Use a dependency just for this (either `lazy_static` or `once_cell`)
  - just seems wrong to use a crate when this is now in the standard library

I think `LazyLock` is just too good to pass on and we should keep it simple
and just use it. From local testing we require 1.74+ anyway so we're "just"
pushing the MSRV 6 Rust releases ahead. And update Rust with `rustup update`
is trivial.

[July 2024]: https://blog.rust-lang.org/2024/07/25/Rust-1.80.0.html
[the alternatives]: rust-lang-nursery/lazy-static.rs#214
xoen added a commit to jnioche/carbonintensity-api that referenced this issue Oct 6, 2024
**NOTE**: This uses `LazyLock` which was stabilised recently in Rust 1.80.
This means we're effectively pushing the MSRV (minimum supported Rust
version) to 1.80+ which is relatively fresh ([July 2024]).

I'm not too keen on [the alternatives]:
1. No nothing: Create a new datetime every time the `validate_date()` is
   called
   - possibly slow/unnecesary, although maybe not a tragedy
2. Use a dependency just for this (either `lazy_static` or `once_cell`)
  - just seems wrong to use a crate when this is now in the standard library

I think `LazyLock` is just too good to pass on and we should keep it simple
and just use it. From local testing we require 1.74+ anyway so we're "just"
pushing the MSRV 6 Rust releases ahead. And update Rust with `rustup update`
is trivial.

[July 2024]: https://blog.rust-lang.org/2024/07/25/Rust-1.80.0.html
[the alternatives]: rust-lang-nursery/lazy-static.rs#214
xoen added a commit to jnioche/carbonintensity-api that referenced this issue Oct 6, 2024
**NOTE**: This uses `LazyLock` which was stabilised recently in Rust 1.80.
This means we're effectively pushing the MSRV (minimum supported Rust
version) to 1.80+ which is relatively fresh ([July 2024]).

I'm not too keen on [the alternatives]:
1. No nothing: Create a new datetime every time the `validate_date()` is
   called
   - possibly slow/unnecesary, although maybe not a tragedy
2. Use a dependency just for this (either `lazy_static` or `once_cell`)
  - just seems wrong to use a crate when this is now in the standard library

I think `LazyLock` is just too good to pass on and we should keep it simple
and just use it. From local testing we require 1.74+ anyway so we're "just"
pushing the MSRV 6 Rust releases ahead. And update Rust with `rustup update`
is trivial.

[July 2024]: https://blog.rust-lang.org/2024/07/25/Rust-1.80.0.html
[the alternatives]: rust-lang-nursery/lazy-static.rs#214
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests