Skip to content

Commit dfe7704

Browse files
Merge pull request #4 from steffahn/sound_rooted_interface_idea
Sound rooted interface idea
2 parents d51192f + 5746000 commit dfe7704

File tree

5 files changed

+244
-90
lines changed

5 files changed

+244
-90
lines changed

Cargo.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@ keywords = ["traversal", "tree", "backtacking", "stack", "cursor"]
1111
categories = ["no-std", "algorithms", "data-structures", "rust-patterns"]
1212

1313
[dependencies]
14+
maybe-dangling = { version = "0.1.1", optional = true}
15+
stable_deref_trait = { version = "1.2.0", default-features = false, optional = true, features = ["alloc"] }
1416

1517
[features]
16-
default = ["alloc"]
17-
alloc = []
18+
default = ["std"]
19+
# Enables the `MutCursorVec` and `MutCursorRootedVec` APIs.
20+
alloc = ["dep:maybe-dangling", "dep:stable_deref_trait"]
21+
# Enables `std` support for `StableDeref`, so you use std-only stable pointers
22+
# without needing to depend on `stable_deref_trait` yourself.
23+
std = ["alloc", "stable_deref_trait/std"]
1824

1925
[package.metadata.docs.rs]
2026
all-features = true

README.md

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
This crate provides types to safely store mutable references to parent nodes, for backtracking during traversal of tree & graph structures.
55

6-
[MutCursor] is more efficient because it avoids dynamic allocation, while [MutCursorVec] provides for an arbitrarily deep stack.
6+
[`MutCursor`] is more efficient because it avoids dynamic allocation, while [`MutCursorVec`] provides for an arbitrarily deep stack.
77

8-
[MutCursorRootedVec] supports mutable references to a separate root type and a different leaf type. In the future I may generalize this pattern to be more flexible. **WARNING** `MutCursorRootedVec` is unsound when the root object owns the memory it references!
8+
[`MutCursorRootedVec`] supports mutable references to a separate root type and a different leaf type. In the future I may generalize this pattern to be more flexible.
99

1010
## Usage
1111
```rust
@@ -49,15 +49,15 @@ impl TreeNode {
4949

5050
This crate basically does the same thing as [generic-cursors](https://crates.io/crates/generic-cursors). However, there are several reasons to choose this crate:
5151

52-
1. The fixed-size stack used by [MutCursor] has lower overhead than a [Vec](https://doc.rust-lang.org/std/vec/struct.Vec.html), and can be used in a `no_std` environment where dynamic allocation may be unavailable.
52+
1. The fixed-size stack used by [`MutCursor`] has lower overhead than a [`Vec`], and can be used in a `no_std` environment where dynamic allocation may be unavailable.
5353

54-
2. The [MutCursor::try_map_into_mut] API enables some paterns that would be otherwise impossible.
54+
2. The [`MutCursor::try_map_into_mut`] API enables some patterns that would be otherwise impossible.
5555

5656
## Safety Thesis
5757

58-
Each `&mut` reference stored by a [MutCursor] mutably borrows the reference beneath it in the stack. The stack root takes a mutable (and therefore exclusive) borrow of the node itself. Therefore the stack's [top](MutCursor::top) is an exclusive borrow.
58+
Each `&mut` reference stored by a [`MutCursor`] mutably borrows the reference beneath it in the stack. The stack root takes a mutable (and therefore exclusive) borrow of the node itself. Therefore the stack's [`top`] is an exclusive borrow.
5959

60-
You can imagine unrolling tree traversal into something like the code below, but this isn't amenable to looping. In essence each `level` variable is preserved, but inaccessible because the level above is mutably borrowing it. The [MutCursor] object contains all the `level` variables but only provides access to the [top](MutCursor::top)
60+
You can imagine unrolling tree traversal into something like the code below, but this isn't amenable to looping. In essence each `level` variable is preserved, but inaccessible because the level above is mutably borrowing it. The [`MutCursor`] object contains all the `level` variables but only provides access to the [`top`].
6161

6262
```rust ignore
6363
let level_1 = &mut root;
@@ -76,12 +76,19 @@ let level_1 = &mut root;
7676

7777
#### Macro to define cursor types
7878

79-
In the current design of [MutCursorRootedVec], there is a predefined pattern prescribing where `RootT` and `NodeT` types may exist on the stack. However, we may find it necessary in the future to support more than two types, in a more flexible pattern. It seems that a macro to define a bespoke cursor type is the best solutuion.
79+
In the current design of [`MutCursorRootedVec`], there is a predefined pattern prescribing where `RootT` and `NodeT` types may exist on the stack. However, we may find it necessary in the future to support more than two types, in a more flexible pattern. It seems that a macro to define a bespoke cursor type is the best solutuion.
8080

8181
#### Internal enum for multiple-type support at runtime
8282

83-
For ultimate flexibility, we would want all the references to be stored by the stack as in an enum over the possible reference types. However, if ther user provided an enum as a type parameter to a cursor type, the result result would be double-indirection. Therefore the enum behavior would need to be internal to the MutCursor. Deriving a MutCursor from a user's enum type feels like a friendly way to define the types a cursor type is capable of storing.
83+
For ultimate flexibility, we would want all the references to be stored by the stack as in an enum over the possible reference types. However, if the user provided an enum as a type parameter to a cursor type, the result result would be double-indirection. Therefore the enum behavior would need to be internal to the MutCursor. Deriving a MutCursor from a user's enum type feels like a friendly way to define the types a cursor type is capable of storing.
8484

8585
## Acknowledgements
8686

87-
[Frank Steffahn](https://github.com/steffahn) identified soundness issues and potential improvements in prior versions of this crate.
87+
[Frank Steffahn](https://github.com/steffahn) identified soundness issues and potential improvements in prior versions of this crate.
88+
89+
[`MutCursor`]: https://docs.rs/mutcursor/latest/mutcursor/struct.MutCursor.html
90+
[`top`]: https://docs.rs/mutcursor/latest/mutcursor/struct.MutCursor.html#method.top
91+
[`MutCursor::try_map_into_mut`]: https://docs.rs/mutcursor/latest/mutcursor/struct.MutCursor.html#method.try_map_into_mut
92+
[`MutCursorVec`]: https://docs.rs/mutcursor/latest/mutcursor/struct.MutCursorVec.html
93+
[`MutCursorRootedVec`]: https://docs.rs/mutcursor/latest/mutcursor/struct.MutCursorRootedVec.html
94+
[`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html

src/lib.rs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,56 @@
1+
// Overrides for `docs.rs` links in the README. This first definition takes precedence.
2+
3+
// fix broken links if documented without `alloc`:
4+
#![cfg_attr(not(feature = "alloc"), doc = "[`MutCursorVec`]: features#alloc")]
5+
#![cfg_attr(not(feature = "alloc"), doc = "[`MutCursorRootedVec`]: features#alloc")]
6+
7+
// more precise links to `std`/`alloc` when possible:
8+
#![cfg_attr(feature = "std", doc = "[Vec]: std::vec::Vec")]
9+
#![cfg_attr(feature = "alloc", doc = "[Vec]: alloc::vec::Vec")]
10+
11+
// Normal link to crate items:
12+
//! [`MutCursor`]: MutCursor
13+
//! [`top`]: MutCursor::top
14+
//! [`MutCursor::try_map_into_mut`]: MutCursor::try_map_into_mut
15+
//! [`MutCursorVec`]: MutCursorVec
16+
//! [`MutCursorRootedVec`]: MutCursorRootedVec
17+
118
#![doc = include_str!("../README.md")]
219

3-
#![no_std]
420
#![cfg_attr(docsrs, feature(doc_cfg))]
521

22+
#![no_std]
23+
24+
#[cfg(any(test, feature = "alloc"))]
25+
extern crate alloc;
26+
#[cfg(any(test, feature = "std"))]
27+
#[cfg_attr(test, macro_use)]
28+
extern crate std;
29+
30+
#[cfg(doc)]
31+
#[cfg_attr(docsrs, doc(cfg(doc)))]
32+
pub mod features {
33+
//! Description of the crate features (not a real module).
34+
//!
35+
//! ## `default`
36+
//! The default features of this crate include `std` and `alloc`
37+
//! ## `alloc`
38+
//! Enables usage of the `alloc` crate, and a public dependency on
39+
//! [`stable_deref_trait`] at version `1.*`
40+
//!
41+
#![cfg_attr(not(feature = "alloc"), doc = "Enables the `MutCursorVec` and `MutCursorRootedVec` APIs.")]
42+
#![cfg_attr(feature = "alloc", doc = "Enables the [`MutCursorVec`] and [`MutCursorRootedVec`] APIs.")]
43+
//! ## `std`
44+
//! Enables `std` support for [`StableDeref`], so you use std-only stable pointers
45+
//! without needing to depend on [`stable_deref_trait`] yourself.
46+
//!
47+
#![cfg_attr(feature = "alloc", doc = "[`stable_deref_trait`]: stable_deref_trait")]
48+
#![cfg_attr(feature = "alloc", doc = "[`StableDeref`]: stable_deref_trait::StableDeref")]
49+
//! [`stable_deref_trait`]: https://docs.rs/stable_deref_trait/1/stable_deref_trait/
50+
//! [`StableDeref`]: https://docs.rs/stable_deref_trait/1/stable_deref_trait/trait.StableDeref.html
51+
use super::*;
52+
}
53+
654
use core::ptr::NonNull;
755
use core::mem::MaybeUninit;
856
use core::marker::PhantomData;
@@ -21,7 +69,7 @@ pub use rooted_vec::*;
2169

2270
/// Stores a stack of `&mut` references, only allowing access to the top element on the stack
2371
///
24-
/// The `MutCursor` stores `N` `&mut T` references, but only allows access to the [top](Self::top)
72+
/// The `MutCursor` stores `N` `&mut T` references, but only allows access to the [`top`](Self::top)
2573
pub struct MutCursor<'root, T: ?Sized + 'root, const N: usize> {
2674
cnt: usize, //The last item cannot be removed, so cnt==0 means there is 1 item
2775
top: usize,
@@ -147,7 +195,7 @@ impl<'root, T: ?Sized + 'root, const N: usize> MutCursor<'root, T, N> {
147195
None => false
148196
}
149197
}
150-
/// Pops a reference from the stack, exposing the prior reference as the new [top](Self::top)
198+
/// Pops a reference from the stack, exposing the prior reference as the new [`top`](Self::top)
151199
///
152200
/// This method will panic if the stack contains only 1 entry
153201
#[inline]
@@ -197,7 +245,6 @@ impl<'root, T: ?Sized, const N: usize> core::ops::DerefMut for MutCursor<'root,
197245

198246
#[cfg(test)]
199247
mod test {
200-
extern crate std;
201248
use std::*;
202249
use std::{boxed::*, vec::Vec, string::String};
203250

src/mut_cursor_vec.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11

22
use core::ptr::NonNull;
33
use core::marker::PhantomData;
4-
extern crate alloc;
54

6-
/// Similar to [MutCursor](crate::MutCursor), but allows for a dynamically growing stack
5+
/// Similar to [`MutCursor`](crate::MutCursor), but allows for a dynamically growing stack
76
///
8-
/// `MutCursorVec` is not available if the `alloc` feature is disabled. (The feature is enabled by default.)
7+
/// `MutCursorVec` is not available if the [`alloc`](crate::features#alloc) feature is disabled.
8+
/// (The feature is enabled by default.)
99
pub struct MutCursorVec<'root, T: ?Sized + 'root> {
1010
top: NonNull<T>,
1111
stack: alloc::vec::Vec<NonNull<T>>,
@@ -65,7 +65,7 @@ impl<'root, T: ?Sized + 'root> MutCursorVec<'root, T> {
6565
}
6666
}
6767
/// Returns the number of excess references stored in the stack, which corresponds to the number of
68-
/// times [backtrack](Self::backtrack) may be called
68+
/// times [`backtrack`](Self::backtrack) may be called
6969
#[inline]
7070
pub fn depth(&self) -> usize {
7171
self.stack.len()
@@ -92,7 +92,7 @@ impl<'root, T: ?Sized + 'root> MutCursorVec<'root, T> {
9292
None => false
9393
}
9494
}
95-
/// Pops a reference from the stack, exposing the prior reference as the new [top](Self::top)
95+
/// Pops a reference from the stack, exposing the prior reference as the new [`top`](Self::top)
9696
///
9797
/// This method will panic if the stack contains only 1 entry
9898
#[inline]
@@ -104,7 +104,7 @@ impl<'root, T: ?Sized + 'root> MutCursorVec<'root, T> {
104104
None => panic!("MutCursor must contain valid reference")
105105
}
106106
}
107-
/// Pops all references from the stack, exposing the root reference as the [top](Self::top)
107+
/// Pops all references from the stack, exposing the root reference as the [`top`](Self::top)
108108
///
109109
/// This method does nothing if the stack is already at the root
110110
#[inline]
@@ -143,7 +143,6 @@ impl<'root, T: ?Sized> core::ops::DerefMut for MutCursorVec<'root, T> {
143143

144144
#[cfg(test)]
145145
mod test {
146-
extern crate std;
147146
use std::*;
148147
use std::boxed::*;
149148

0 commit comments

Comments
 (0)