pub struct WriteOnce<T> {
value: UnsafeCell<MaybeUninit<T>>,
state: AtomicU8,
}Expand description
A thread-safe, wait-free, write-once cell that allows a value to be set exactly once.
WriteOnce provides a way to initialize a value exactly once in a thread-safe manner. Once a
value is set, it cannot be changed. This is useful in scenarios where you need to ensure that a
value is initialized exactly once, even in the presence of concurrent access.
WriteOnce is similar to std::sync::OnceLock, but with a key difference: it takes a value
directly rather than a closure that initializes the value. In other words, we assume an
optimistic concurrency model, contrary to OnceLock’s pessimistic model.
This concurrency model allows WriteOnce to be fully wait-free. This also implies that, whereas
OnceLock guarantees that the closure is called at most once, WriteOnce guarantees that the
value is set at most once.
§Thread Safety
WriteOnce is designed to be thread-safe and wait-free, allowing concurrent attempts to set the
value from multiple threads. Only the first successful call to set or try_set will
initialize the value; subsequent attempts will either panic (with set) or return an error
(with try_set).
§Memory Safety
WriteOnce uses atomic operations to ensure memory safety and proper synchronization between
threads. It guarantees that:
- A value can be set exactly once
- Once set, the value can be safely read from any thread
- The value is properly dropped when the
WriteOnceis dropped
§Examples
use once::write_once::WriteOnce;
// Create a new WriteOnce with no value
let cell = WriteOnce::<String>::none();
// Set the value
cell.set("Hello, world!".to_string());
// Get the value
assert_eq!(cell.get(), Some(&"Hello, world!".to_string()));
// Attempting to set the value again will panic
// cell.set("Another value".to_string()); // This would panic
// Using try_set instead returns an error
let result = cell.try_set("Another value".to_string());
assert!(result.is_err());
if let Err(value) = result {
assert_eq!(value, "Another value".to_string());
}Fields§
§value: UnsafeCell<MaybeUninit<T>>§state: AtomicU8Implementations§
Source§impl<T> WriteOnce<T>
impl<T> WriteOnce<T>
Sourcepub fn new(value: T) -> Self
pub fn new(value: T) -> Self
Creates a new WriteOnce with a value.
§Examples
use once::write_once::WriteOnce;
let cell = WriteOnce::new(2);
assert_eq!(cell.get(), Some(&2));
assert!(cell.is_set());Sourcepub fn set(&self, value: T)
pub fn set(&self, value: T)
Sets the value of the WriteOnce.
This method sets the value of the cell if it hasn’t been set yet. If the cell already has a value, this method will panic.
§Panics
Panics if the cell already has a value.
§Examples
use once::write_once::WriteOnce;
let cell = WriteOnce::<i32>::none();
cell.set(42);
assert_eq!(cell.get(), Some(&42));
// Uncommenting the following line would cause a panic:
// cell.set(100);Sourcepub fn try_set(&self, value: T) -> Result<(), T>
pub fn try_set(&self, value: T) -> Result<(), T>
Attempts to set the value of the WriteOnce.
This method tries to set the value of the cell if it hasn’t been set yet. If the cell already has a value, this method will return an error containing the value that was attempted to be set.
§Returns
Ok(())if the value was successfully setErr(value)if the cell already had a value, returning the value that was attempted to be set
§Examples
use once::write_once::WriteOnce;
let cell = WriteOnce::<String>::none();
// First attempt succeeds
let result = cell.try_set("Hello".to_string());
assert!(result.is_ok());
assert_eq!(cell.get(), Some(&"Hello".to_string()));
// Second attempt fails
let result = cell.try_set("World".to_string());
assert!(result.is_err());
if let Err(value) = result {
assert_eq!(value, "World".to_string());
}
// The value remains unchanged
assert_eq!(cell.get(), Some(&"Hello".to_string()));Sourcepub fn get(&self) -> Option<&T>
pub fn get(&self) -> Option<&T>
Gets the value of the WriteOnce.
Returns Some(&T) if the cell has a value, or None if it doesn’t.
§Examples
use once::write_once::WriteOnce;
let cell = WriteOnce::<i32>::none();
assert_eq!(cell.get(), None);
cell.set(42);
assert_eq!(cell.get(), Some(&42));Sourcepub unsafe fn get_unchecked(&self) -> &T
pub unsafe fn get_unchecked(&self) -> &T
§Safety
This method is unsafe because it returns a reference to the value without checking if the value is initialized. The caller must ensure that the value is initialized before calling this method.
Sourcepub fn is_set(&self) -> bool
pub fn is_set(&self) -> bool
Checks if the WriteOnce has a value.
Returns true if the cell has a value, or false if it doesn’t.
This method only returns true if the value has been set and is ready to be read. In
particular, it will return false if the value is currently being set by another thread.
This may be relevant if the value is a large object and moving it in memory is expensive.
§Examples
use once::write_once::WriteOnce;
let cell = WriteOnce::<i32>::none();
assert!(!cell.is_set());
cell.set(42);
assert!(cell.is_set());Sourcepub fn get_mut(&mut self) -> Option<&mut T>
pub fn get_mut(&mut self) -> Option<&mut T>
Gets a mutable reference to the value of the WriteOnce.
Returns Some(&mut T) if the cell has a value, or None if it doesn’t.
This method requires a mutable reference to the WriteOnce, which ensures
that no other thread is accessing the value.
§Examples
use once::write_once::WriteOnce;
let mut cell = WriteOnce::<String>::none();
assert_eq!(cell.get_mut(), None);
cell.set("Hello".to_string());
// Modify the value through a mutable reference
if let Some(value) = cell.get_mut() {
value.push_str(", world!");
}
assert_eq!(cell.get(), Some(&"Hello, world!".to_string()));Trait Implementations§
Source§impl<T: Ord> Ord for WriteOnce<T>
impl<T: Ord> Ord for WriteOnce<T>
Source§impl<T: PartialOrd> PartialOrd for WriteOnce<T>
impl<T: PartialOrd> PartialOrd for WriteOnce<T>
impl<T: Eq> Eq for WriteOnce<T>
impl<T: Send> Send for WriteOnce<T>
impl<T: Sync> Sync for WriteOnce<T>
Auto Trait Implementations§
impl<T> !Freeze for WriteOnce<T>
impl<T> !RefUnwindSafe for WriteOnce<T>
impl<T> Unpin for WriteOnce<T>where
T: Unpin,
impl<T> UnwindSafe for WriteOnce<T>where
T: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more