| Crates.io | wrapit |
| lib.rs | wrapit |
| version | 0.2.0 |
| created_at | 2025-10-10 19:28:47.733108+00 |
| updated_at | 2025-10-12 07:41:54.367322+00 |
| description | A lightweight Rust crate that wraps any variable using interior mutability (`RefCell`, `Mutex`, or `RwLock`) for ergonomic shared mutation. |
| homepage | https://github.com/2teez/wrapit |
| repository | https://github.com/2teez/wrapit |
| max_upload_size | |
| id | 1877477 |
| size | 20,088 |
wrapit is a small Rust library that wraps any variable using interior mutability to allow mutation through shared references.
This crate provides a generic Wrapper
Note: This crate uses RefCell or Mutex for interior mutability. Using RefCell is not thread-safe because it does not implement the Send and Sync traits. Concurrent access across threads will ONLY compile, if you use SyncWrapper
You can install wrapit either from Crates.io or directly from the Git repository.
Cargo.toml file.wrapit to your dependencies:[dependencies]
wrapit = "0.2.0"
OR do;
cargo add wrapit
from your project folder or directory.
Cargo.toml file.wrapit as a dependency using the Git repository URL:[dependencies]
wrapit = { git = "https://github.com/2teez/wrapit.git" }
use wrapit::Wrapper; // Not thread-safe
use wrapit::SyncWrapper; // thread-safe
OR
use wrapit::{Wrapper, SyncWrapper};
wrapit allows you to wrap any value and mutate it through a shared reference, similar to JavaScript or Python variables. You can modify the value using the reset method and read it using get, without needing the mut keyword on the wrapper itself.
wrapit provides two wrapper types: Wrapper and SyncWrapper. Both share a similar API and serve the same purpose of encapsulating a value for controlled mutation.
The key difference lies in their safety guarantees:
Wrapper uses non-thread-safe interior mutability and exposes a borrow method.
SyncWrapper is thread-safe and exposes a lock method for synchronized access.
// in javascript
const langs = ["java", "clojure", "lua"];
langs.push("swift");
console.log(langs);
However in rust, you will have to use the mut keyword with the variable like so:
let mut langs = vec!["java", "clojure", "lua"];
langs.push("swift");
println!("{:?}", langs);
using wrapit, do this:
use wrapit::Wrapper;
let langs = vec!["java", "clojure", "lua"]; // immutable
let wrapper = Wrapper::new(langs);
// changed the variable content without the `mut` keyword
wrapper.reset(vec!["java", "clojure", "lua", "swift"]);
println!("{:?}", wrapper);
OR
use std::thread;
use wrapit::SyncWrapper;
// Create a thread-safe wrapper for an integer
let counter = SyncWrapper::new(0);
// Read-only access using map
counter.map(|x| {
println!("Current value: {}", x);
});
// Modify the value safely using lock
{
let mut guard = counter.lock(); // no need for `mut` on counter itself
*guard = 1;
println!("Locked value: {:?}", *guard);
} // lock released here
// Using a thread to read the value without mut
let counter_clone = counter.clone();
thread::spawn(move || {
counter_clone.map(|x| println!("Thread sees: {}", x));
})
.join()
.unwrap();
// Replace the value using reset
counter.reset(42);
println!("After reset, value: {}", counter.get());
}
pub fn new(data: T) -> Self
- Creates a new Wrapper
containing data. - Allocates the value inside RefCell and wraps it in Arc.
let wrapper = Wrapper::new(42);
pub fn reset(&self, ndata: T) -> &Self
- Replaces the inner value with ndata.
- Returns a reference to self for method chaining.
- Panics at runtime if any active borrow exists (e.g., if .borrow() has been called and not dropped).
wrapper.reset(100);
pub fn get(&self) -> T
- Returns a clone of the inner value.
- Uses immutable borrow (borrow()) internally.
- Panics if a mutable borrow is active.
let value = wrapper.get();
pub fn borrow(&self) -> std::cell::Ref<'_, T>
- Returns a Ref
, a smart pointer for immutable access. - Automatically releases the borrow when dropped.
- Panics if a mutable borrow is active.
let value_ref = wrapper.borrow();
println!("{}", *value_ref);
pub fn map<F, U>(&self, f: F) -> U
where
F: FnOnce(&T) -> U,
- Applies a function f to the inner value.
- Returns the function’s result.
- Panics if a mutable borrow exists while calling this method.
let len = wrapper.map(|s| s.len());
| Method | Cause of Panic |
|---|---|
| reset() | Active immutable borrow exists |
| get() | Active mutable borrow exists |
| borrow() | Active mutable borrow exists |
| map() | Active mutable borrow exists |
- These panics are enforced at runtime by RefCell.
use wrapped::wrapped::Wrapper;
let w = Wrapper::new(String::from("Rust"));
// Read value
assert_eq!(w.get(), "Rust");
// Mutate value
w.reset(String::from("C++"));
assert_eq!(w.get(), "C++");
// Borrow inner value
let val = w.borrow();
println!("Value = {}", *val);
// Map function
let len = w.map(|s| s.len());
assert_eq!(len, 3);
- This Wrapper is not thread-safe. If you want wrapper which is thread-safe use SyncWrapper instead.
- All borrows are checked at runtime using RefCell.
- Use .borrow() and .borrow_mut() carefully to avoid panics.
- Cloning the Wrapper shares the same underlying value.
pub fn new(data: T) -> Self
- Creates a new thread-safe SyncWrapper containing the provided value.
- Allocates the value inside RefCell and wraps it in Arc.
- No runtime panic.
use wrapit::SyncWrapper;
let wrapper = SyncWrapper::new("clojure");
assert_eq!(wrapper.get(), "clojure");
reset
pub fn reset(&self, ndata: T) -> &Self
- The
resetmethod ofSyncWrapperreplaces the current value inside the wrapper with a new one.- Acquires a lock on the internal
Mutex<T>.- Replaces the current value with
ndata.- Automatically releases the lock when the operation completes.
- Panics if the mutex is poisoned (i.e., another thread panicked while holding the lock).
use wrapit::SyncWrapper;
fn main() {
let wrapper = SyncWrapper::new(10);
// Reset the value
wrapper.reset(42);
// Verify
assert_eq!(wrapper.get(), 42);
// Method chaining
wrapper.reset(100).reset(200);
assert_eq!(wrapper.get(), 200);
}
reset does not give direct mutable access to the internal value.
To modify the value directly, use lock() instead.
Useful for simple replacement of the value without explicit locking.
Works safely across threads due to internal Mutex.
get
pub fn get(&self) -> T
- The
getmethod ofSyncWrapperreturns a clone of the current value stored inside the wrapper.- Acquires a lock on the internal
Mutex<T>.- Clones and returns the value.
- Automatically releases the lock when done.
- Panics if the mutex is poisoned (i.e., another thread panicked while holding the lock).
use wrapit::SyncWrapper;
fn main() {
let wrapper = SyncWrapper::new(10);
// Read the value
let val = wrapper.get();
assert_eq!(val, 10);
// The wrapper is still accessible after getting the value
wrapper.reset(42);
assert_eq!(wrapper.get(), 42);
}
get provides a safe, read-only snapshot of the value.
The returned value is a clone; modifying it does not affect the wrapper.
To modify the value directly, use lock() or reset().
lock
pub fn lock(&self) -> std::sync::MutexGuard<'_, T>
- The
lockmethod ofSyncWrapperprovides mutable access to the value inside the wrapper by returning aMutexGuard.- Acquires a lock on the internal
Mutex<T>.- Returns a
MutexGuardthat can be used to read or modify the value.- Automatically releases the lock when the guard goes out of scope.
- Panics if the mutex is poisoned (i.e., another thread panicked while holding the lock).
use wrapit::SyncWrapper;
fn main() {
let wrapper = SyncWrapper::new(vec![1, 2, 3]);
{
let mut guard = wrapper.lock();
guard.push(4); // modify directly
println!("Locked value: {:?}", *guard);
} // lock released here automatically
assert_eq!(wrapper.get(), vec![1, 2, 3, 4]);
}
Use lock() when you need direct mutable access to the value.
The lock is released automatically when the MutexGuard goes out of scope.
Safe to call from multiple threads; each will block until the mutex is available.
Avoid holding the lock across long operations or thread joins to prevent deadlocks.
map
pub fn map<F, U>(&self, f: F) -> U
where
F: FnOnce(&T) -> U
- The
mapmethod ofSyncWrapperallows you to apply a function to the wrapped value without exposing theMutexGuarddirectly.- Acquires a lock on the internal
Mutex<T>.- Calls the provided function
fwith a reference to the value.- Automatically releases the lock after the function call.
- Panics if the mutex is poisoned (i.e., another thread panicked while holding the lock).
use wrapit::SyncWrapper;
fn main() {
let wrapper = SyncWrapper::new(10);
// Read-only computation using map
let doubled = wrapper.map(|x| x * 2);
assert_eq!(doubled, 20);
// Original value remains unchanged
assert_eq!(wrapper.get(), 10);
// Can also read a vector safely
let w = SyncWrapper::new(vec![1, 2, 3]);
let length = w.map(|v| v.len());
assert_eq!(length, 3);
}
map is useful when you want temporary, controlled access to the value without exposing the MutexGuard.
The lock is released immediately after the closure finishes.
Safe to use concurrently across multiple threads.
Ideal for read-only operations or computations that do not need to mutate the value directly.
0.1.0 - wrapit with non thread-safe wrapper.
0.1.1 - README.md file wordings changed.
0.2.0 - wrapit with thread-safe wrapper included, with the README.md file updated to reflect the change.