Skip to content

Commit ac74844

Browse files
committed
Add README.md
1 parent beefbd4 commit ac74844

1 file changed

Lines changed: 110 additions & 0 deletions

File tree

README.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# RefMutStack
2+
[![Latest Version](https://img.shields.io/crates/v/ref_mut_stack)](https://crates.io/crates/ref_mut_stack)
3+
[![Documentation](https://docs.rs/ref_mut_stack/badge.svg)](https://docs.rs/ref_mut_stack)
4+
[![Build Status](https://github.com/arnodb/ref_mut_stack/actions/workflows/ci.yml/badge.svg)](https://github.com/arnodb/ref_mut_stack/actions/workflows/ci.yml)
5+
[![Code Coverage](https://codecov.io/gh/arnodb/ref_mut_stack/branch/main/graph/badge.svg)](https://codecov.io/gh/arnodb/ref_mut_stack)
6+
7+
RefMutStack allows to simulate recursion where each level holds a mutable reference to the one held by the caller with an iteration.
8+
9+
It is made in such a way that the rules enforced by the borrow checker during the theoretical recursion are still enforced during iterations. On that purpose, each object holding a mutable reference becomes unreachable when the recursion is similated: it is stacked until it becomes usable again.
10+
11+
## Soundness
12+
13+
RefMutStack should be sound in many ways and some tests are implemented to ensure it remains sound. However there might be unsound cases which would very likely be attempts at abusing it - we don't judge.
14+
15+
The "`impl Drop` using the held mutable reference" case is even protected but note that it would easily qualify as "abuse". Keep it simple and everything will be fine.
16+
17+
## Example
18+
19+
[owned_builder.rs](examples/owned_builder.rs) shows an example of a builder used to iteratively build a tree which should be built recursively otherwise (to leverage the true borrow checker):
20+
21+
```rust
22+
use ref_mut_stack::{ParkableRefMut, RefMutStack};
23+
24+
#[derive(Default, Debug, PartialEq, Eq)]
25+
struct Node {
26+
value: usize,
27+
child: Option<Box<Node>>,
28+
built_value: usize,
29+
}
30+
31+
struct Builder<'a> {
32+
node: ParkableRefMut<'a, Node, Self>,
33+
}
34+
35+
impl<'a> Builder<'a> {
36+
fn value(mut self, value: usize) -> Self {
37+
self.node.value = value;
38+
self
39+
}
40+
41+
fn new_child(mut self) -> Self {
42+
if self.node.child.is_some() {
43+
panic!();
44+
}
45+
self.node.child = Some(Box::new(Node::default()));
46+
let child = self
47+
.node
48+
.parker()
49+
.park(self, |node| node.child.as_mut().unwrap());
50+
Self { node: child }
51+
}
52+
53+
fn build(mut self) -> Option<Self> {
54+
self.node.built_value = self
55+
.node
56+
.child
57+
.as_ref()
58+
.map_or(self.node.value, |child| child.built_value + 1);
59+
self.node.unpark()
60+
}
61+
}
62+
63+
#[test]
64+
fn test_owned_builder() {
65+
let mut root = Node::default();
66+
67+
let mut stack = RefMutStack::new(&mut root);
68+
let b = Builder {
69+
node: stack.borrow_mut(),
70+
}
71+
.value(1)
72+
.new_child()
73+
.value(2)
74+
.new_child()
75+
.value(3)
76+
.build()
77+
.unwrap()
78+
.build()
79+
.unwrap()
80+
.build();
81+
if b.is_some() {
82+
panic!()
83+
}
84+
85+
assert_eq!(
86+
root,
87+
Node {
88+
value: 1,
89+
child: Some(Box::new(Node {
90+
value: 2,
91+
child: Some(Box::new(Node {
92+
value: 3,
93+
child: None,
94+
built_value: 3
95+
})),
96+
built_value: 4
97+
})),
98+
built_value: 5
99+
}
100+
);
101+
}
102+
```
103+
104+
## Note from the author
105+
106+
I created this because I needed to traverse a tree branch and do something on the leaf node and was pretty annoyed I needed recursion and callbacks to leverage the borrow checker.
107+
108+
RefMutStack enables me to change my implementation to an iteration and a call to manipulate the leaf node.
109+
110+
Yes, it replaces the thread stack by a stack on the heap. Some would think it is bad, some would find it cool, I don't judge.

0 commit comments

Comments
 (0)