From 8c42266612bdf8493fb48bb77fe8c8c16cdfc116 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 23 May 2023 12:10:59 +0100 Subject: [PATCH] modernize docs closes #229 --- src/lib.rs | 111 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 40 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 00cdd89..9239296 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,10 @@ //! # Overview //! -//! `once_cell` provides two new cell-like types, [`unsync::OnceCell`] and [`sync::OnceCell`]. A `OnceCell` -//! might store arbitrary non-`Copy` types, can be assigned to at most once and provides direct access -//! to the stored contents. The core API looks *roughly* like this (and there's much more inside, read on!): +//! `once_cell` provides two new cell-like types, [`unsync::OnceCell`] and +//! [`sync::OnceCell`]. A `OnceCell` might store arbitrary non-`Copy` types, can +//! be assigned to at most once and provides direct access to the stored +//! contents. The core API looks *roughly* like this (and there's much more +//! inside, read on!): //! //! ```rust,ignore //! impl OnceCell { @@ -12,11 +14,12 @@ //! } //! ``` //! -//! Note that, like with [`RefCell`] and [`Mutex`], the `set` method requires only a shared reference. -//! Because of the single assignment restriction `get` can return a `&T` instead of `Ref` -//! or `MutexGuard`. +//! Note that, like with [`RefCell`] and [`Mutex`], the `set` method requires +//! only a shared reference. Because of the single assignment restriction `get` +//! can return a `&T` instead of `Ref` or `MutexGuard`. //! -//! The `sync` flavor is thread-safe (that is, implements the [`Sync`] trait), while the `unsync` one is not. +//! The `sync` flavor is thread-safe (that is, implements the [`Sync`] trait), +//! while the `unsync` one is not. //! //! [`unsync::OnceCell`]: unsync/struct.OnceCell.html //! [`sync::OnceCell`]: sync/struct.OnceCell.html @@ -79,7 +82,8 @@ //! } //! ``` //! -//! There are also the [`sync::Lazy`] and [`unsync::Lazy`] convenience types to streamline this pattern: +//! There are also the [`sync::Lazy`] and [`unsync::Lazy`] convenience types to +//! streamline this pattern: //! //! ```rust //! use std::{sync::Mutex, collections::HashMap}; @@ -120,7 +124,8 @@ //! ``` //! //! If you need a lazy field in a struct, you probably should use `OnceCell` -//! directly, because that will allow you to access `self` during initialization. +//! directly, because that will allow you to access `self` during +//! initialization. //! //! ```rust //! use std::{fs, path::PathBuf}; @@ -156,7 +161,8 @@ //! } //! ``` //! -//! This macro can be useful to avoid the "compile regex on every loop iteration" problem. +//! This macro can be useful to avoid the "compile regex on every loop +//! iteration" problem. //! //! ## Runtime `include_bytes!` //! @@ -243,7 +249,7 @@ //! let b = B::default(); //! a.b.init(&b); //! b.a.init(&a); -//! +//! //! let _a = &a.b.a.b.a; //! } //! ``` @@ -262,23 +268,28 @@ //! |`Mutex` | `MutexGuard` | may deadlock at runtime, may block the thread | //! |`sync::OnceCell` | `&T` | assignable only once, may block the thread | //! -//! Technically, calling `get_or_init` will also cause a panic or a deadlock if it recursively calls -//! itself. However, because the assignment can happen only once, such cases should be more rare than -//! equivalents with `RefCell` and `Mutex`. +//! Technically, calling `get_or_init` will also cause a panic or a deadlock if +//! it recursively calls itself. However, because the assignment can happen only +//! once, such cases should be more rare than equivalents with `RefCell` and +//! `Mutex`. //! //! # Minimum Supported `rustc` Version //! //! This crate's minimum supported `rustc` version is `1.56.0`. //! -//! If only the `std` feature is enabled, MSRV will be updated conservatively, supporting at least latest 8 versions of the compiler. -//! When using other features, like `parking_lot`, MSRV might be updated more frequently, up to the latest stable. -//! In both cases, increasing MSRV is *not* considered a semver-breaking change. +//! If only the `std` feature is enabled, MSRV will be updated conservatively, +//! supporting at least latest 8 versions of the compiler. When using other +//! features, like `parking_lot`, MSRV might be updated more frequently, up to +//! the latest stable. In both cases, increasing MSRV is *not* considered a +//! semver-breaking change. //! //! # Implementation details //! -//! The implementation is based on the [`lazy_static`](https://github.com/rust-lang-nursery/lazy-static.rs/) -//! and [`lazy_cell`](https://github.com/indiv0/lazycell/) crates and [`std::sync::Once`]. In some sense, -//! `once_cell` just streamlines and unifies those APIs. +//! The implementation is based on the +//! [`lazy_static`](https://github.com/rust-lang-nursery/lazy-static.rs/) and +//! [`lazy_cell`](https://github.com/indiv0/lazycell/) crates and +//! [`std::sync::Once`]. In some sense, `once_cell` just streamlines and unifies +//! those APIs. //! //! To implement a sync flavor of `OnceCell`, this crates uses either a custom //! re-implementation of `std::sync::Once` or `parking_lot::Mutex`. This is @@ -292,46 +303,66 @@ //! //! # F.A.Q. //! -//! **Should I use lazy_static or once_cell?** +//! **Should I use the sync or unsync flavor?** //! -//! To the first approximation, `once_cell` is both more flexible and more convenient than `lazy_static` -//! and should be preferred. +//! Because Rust compiler checks thread safety for you, it's impossible to +//! accidentally use `unsync` where `sync` is required. So, use `unsync` in +//! single-threaded code and `sync` in multi-threaded. It's easy to switch +//! between the two if code becomes multi-threaded later. //! -//! Unlike `once_cell`, `lazy_static` supports spinlock-based implementation of blocking which works with -//! `#![no_std]`. +//! At the moment, `unsync` has an additional benefit that reentrant +//! initialization causes a panic, which might be easier to debug than a +//! deadlock. //! -//! `lazy_static` has received significantly more real world testing, but `once_cell` is also a widely -//! used crate. +//! **Does this crate support async?** //! -//! **Should I use the sync or unsync flavor?** +//! No, but you can use +//! [`async_once_cell`](https://crates.io/crates/async_once_cell) instead. //! -//! Because Rust compiler checks thread safety for you, it's impossible to accidentally use `unsync` where -//! `sync` is required. So, use `unsync` in single-threaded code and `sync` in multi-threaded. It's easy -//! to switch between the two if code becomes multi-threaded later. +//! **Does this crate support `no_std`?** //! -//! At the moment, `unsync` has an additional benefit that reentrant initialization causes a panic, which -//! might be easier to debug than a deadlock. +//! Yes, but with caveats. `OnceCell` is a synchronization primitive which +//! _semantically_ relies on blocking. `OnceCell` guarantees that at most one +//! `f` will be called to compute the value. If two threads of execution call +//! `get_or_init` concurrently, one of them has to wait. //! -//! **Does this crate support async?** +//! Waiting fundamentally requires OS support. Execution environment needs to +//! understand who waits on whom to prevent deadlocks due to priority inversion. +//! You _could_ make code to compile by blindly using pure spinlocks, but the +//! runtime behavior would be subtly wrong. //! -//! No, but you can use [`async_once_cell`](https://crates.io/crates/async_once_cell) instead. +//! Given these constraints, `once_cell` provides the following options: +//! +//! - The `race` module provides similar, but distinct synchronization primitive +//! which is compatible with `no_std`. With `race`, the `f` function can be +//! called multiple times by different threads, but only one thread will win +//! to install the value. +//! - `critical-section` feature (with a `-`, not `_`) uses `critical_section` +//! to implement blocking. //! //! **Can I bring my own mutex?** //! -//! There is [generic_once_cell](https://crates.io/crates/generic_once_cell) to allow just that. +//! There is [generic_once_cell](https://crates.io/crates/generic_once_cell) to +//! allow just that. +//! +//! **Should I use `std::cell::OnceCell`, `once_cell`, or `lazy_static`?** +//! +//! If you can use `std` version (your MSRV is at least 1.70, and you don't need +//! extra features `once_cell` provides), use `std`. Otherwise, use `once_cell`. +//! Don't use `lazy_static`. //! //! # Related crates //! +//! * Most of this crate's functionality is available in `std` starting with +//! Rust 1.70. See `std::cell::OnceCell` and `std::sync::OnceLock`. //! * [double-checked-cell](https://github.com/niklasf/double-checked-cell) //! * [lazy-init](https://crates.io/crates/lazy-init) //! * [lazycell](https://crates.io/crates/lazycell) //! * [mitochondria](https://crates.io/crates/mitochondria) //! * [lazy_static](https://crates.io/crates/lazy_static) //! * [async_once_cell](https://crates.io/crates/async_once_cell) -//! * [generic_once_cell](https://crates.io/crates/generic_once_cell) (bring your own mutex) -//! -//! Most of this crate's functionality is available in `std` in nightly Rust. -//! See the [tracking issue](https://github.com/rust-lang/rust/issues/74465). +//! * [generic_once_cell](https://crates.io/crates/generic_once_cell) (bring +//! your own mutex) #![cfg_attr(not(feature = "std"), no_std)]