ratio-clone

Crates.ioratio-clone
lib.rsratio-clone
version0.2.0
created_at2025-12-03 11:21:09.26191+00
updated_at2025-12-03 15:12:03.852662+00
descriptionA library for cheap-to-clone types and wrappers for expensive to clone and compare datatypes.
homepagehttps://gitlab.com/ratio-case-os/rust/ratio-case/-/tree/main/ratio-clone
repositoryhttps://gitlab.com/ratio-case-os/rust/ratio-case.git
max_upload_size
id1963885
size41,259
Tiemen Schuijbroek (TiemenSch)

documentation

README

Ratio's cheap cloning library

A library for cheap-to-clone types and wrappers for expensive to clone and compare datatypes.

It's mainly used by Ratio to facilitate cheap cloning and doing crude change detection in reactive applications such as data storage in WebApps (i.e. Yew, Leptos).

Heavily inspired by the work over at Yewdux on their Mrc implementation.

Examples

Single threaded

Single threaded usage relies on Rc and RefCell for interior mutability and Cell<usize> for the counter. You won't see much of them, though.

use ratio_clone::{Mrc, BorrowDeref, BorrowDerefMut, With, WithInnerMut};
use std::ops::{Deref, DerefMut};

let original = Mrc::new_refcell("Something that would be very expensive to clone.".to_string());
let clone_a = original.clone();
let clone_b = original.clone();

assert!(original == clone_a, "should be equal after a plain clone");

// Borrowing mutably always increases an internal counter.
// It plainly assumes you changed something.
// Even if you do nothing!
clone_a.with_inner_mut(|a_mut|{ *a_mut += " But let's add something."; });
// Alternatively:
let a_mut = clone_a.borrow_mut().deref_mut();

// Borrowing immutably is just fine and wont update the counter.
clone_b.with(|b|{});
// Alternatively:
let b = clone_b.borrow().deref();

assert!(original != clone_a, "Clone A had mutable access, so it is assumed to be different.");
assert!(original == clone_b, "Clone B is still identical to the original.");

// You CAN check interior equality if the type supports it.
use ratio_clone::InnerEq;
let clone_c = original.clone();
clone_c.with_inner_mut(|c_mut|{});
assert!(clone_c != original, "We borrowed mutably, so the Clone C's counter states inequality.");
assert!(original.inner_eq(&clone_c), "Inner comparison still confirms equality.");

Multi-threaded/atomic

Single threaded usage relies on Arc and RwLock or Mutex for interior mutability and AtomicUsize for the counter. These should be fairly hidden from view as well;

use ratio_clone::{Marc, BorrowDeref, BorrowDerefMut, With, WithInnerMut};
use std::ops::{Deref, DerefMut};

let original = Marc::new_rw("Something that would be very expensive to clone.".to_string());
let clone_a = original.clone();
let clone_b = original.clone();

assert!(original == clone_a, "should be equal after a plain clone");

// Borrowing mutably always increases an internal counter.
// It plainly assumes you changed something.
// Even if you do nothing!
clone_a.with_inner_mut(|a_mut|{ *a_mut += " But let's add something."; });
// Alternatively:
let a_mut = clone_a.borrow_mut().deref_mut();

// Borrowing immutably is just fine and wont update the counter.
clone_b.with(|b|{});
// Alternatively:
let b = clone_b.borrow().deref();

assert!(original != clone_a, "Clone A had mutable access, so it is assumed to be different.");
assert!(original == clone_b, "Clone B is still identical to the original.");


// You CAN check interior equality if the type supports it.
use ratio_clone::InnerEq;
let clone_c = original.clone();
clone_c.with_inner_mut(|c_mut|{});
assert!(clone_c != original, "We borrowed mutably, so the Clone C's counter states inequality.");
assert!(original.inner_eq(&clone_c), "Inner comparison still confirms equality.");

Warning: Be careful with the Mutex variant, as it will block on simultaneous reads. That is also why [InnerEq] is available for [Mrc] and [Marc] with RwLock, but not for the Mutex. Acquiring a reference via the same mutex twice will result in a deadlock.

Note the small change!

Note how easy it is to transform from Mrc to Marc! Just a different original and you're good to go!

Documentation

Please refer to the crate's documentation on docs.rs for more information on it's usage.

Changelog

This repository keeps a CHANGELOG.md according to the recommendations by Keep a Changelog.

Contributions

Contributions are welcome! By submitting a contribution, you agree to license your work under the terms of the Mozilla Public License 2.0. Please ensure that your contributions adhere to the existing code style and include appropriate tests and documentation where applicable.

To get started:

  1. Fork the repo
  2. Create a new branch
  3. Make your changes
  4. Make sure you run just fix to adhere to the project's formatting
  5. Submit a merge request with a clear description of the changes

Licensing

This project is licensed under the Mozilla Public License 2.0. You are free to use, modify, and distribute this code, provided that any files you modify or create that are based on MPL-licensed files also remain under the MPL. You must include a copy of the license with the source and make the source code available when distributing binaries.

See the LICENSE file for the full license text.

Code examples both in the docstrings and rendered documentation thereof are free to use!

At Ratio, we are huge supporters of open-source code and the open-source community. In our Python projects we usually strive to use one of the (L)GPL flavors. These are difficult to pair with compiled codebases, however, which is where we see the MPL-2.0 as a great fit for our open-source Rust efforts. It's a weak copyleft license that just protects the source as it is written and encourages changes to the crate's source to be published accordingly. It's sort of "automagically" implied and done right when cargo would pull in the source files to build with, as (the mentioning of) the license is included in the header of each file, and any binaries you generate with them are not of our concern from a distribution perspective.

Enjoy the code!

Commit count: 176

cargo fmt