Skip to content

Commit 72a0eac

Browse files
authored
fix: Tree now tied to TreeSequence lifetime (#497)
* Add compile fail test of tree/treeseq liftimes
1 parent 7e4a3d0 commit 72a0eac

File tree

2 files changed

+33
-11
lines changed

2 files changed

+33
-11
lines changed

examples/haploid_wright_fisher.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ proptest! {
161161
assert!(b2 >= 0, "{}", b2);
162162
assert!(f64::from(b) - x <= 1e-8);
163163
}
164-
}
164+
};
165165
}
166166
}
167167
}

src/trees.rs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,42 @@ use std::ptr::NonNull;
1919
/// A Tree.
2020
///
2121
/// Wrapper around `tsk_tree_t`.
22-
pub struct Tree {
22+
pub struct Tree<'treeseq> {
2323
pub(crate) inner: mbox::MBox<ll_bindings::tsk_tree_t>,
24+
// NOTE: this reference exists becaust tsk_tree_t
25+
// contains a NON-OWNING pointer to tsk_treeseq_t.
26+
// Thus, we could theoretically cause UB without
27+
// tying the rust-side object liftimes together.
28+
#[allow(dead_code)]
29+
treeseq: &'treeseq TreeSequence,
2430
api: TreeInterface,
2531
current_tree: i32,
2632
advanced: bool,
2733
}
2834

29-
impl Drop for Tree {
35+
impl<'treeseq> Drop for Tree<'treeseq> {
3036
fn drop(&mut self) {
3137
// SAFETY: Mbox<_> cannot hold a NULL ptr
3238
let rv = unsafe { tsk_tree_free(self.inner.as_mut()) };
3339
assert_eq!(rv, 0);
3440
}
3541
}
3642

37-
impl Deref for Tree {
43+
impl<'treeseq> Deref for Tree<'treeseq> {
3844
type Target = TreeInterface;
3945
fn deref(&self) -> &Self::Target {
4046
&self.api
4147
}
4248
}
4349

44-
impl DerefMut for Tree {
50+
impl<'treeseq> DerefMut for Tree<'treeseq> {
4551
fn deref_mut(&mut self) -> &mut Self::Target {
4652
&mut self.api
4753
}
4854
}
4955

50-
impl Tree {
51-
fn new<F: Into<TreeFlags>>(ts: &TreeSequence, flags: F) -> Result<Self, TskitError> {
56+
impl<'treeseq> Tree<'treeseq> {
57+
fn new<F: Into<TreeFlags>>(ts: &'treeseq TreeSequence, flags: F) -> Result<Self, TskitError> {
5258
let flags = flags.into();
5359

5460
// SAFETY: this is the type we want :)
@@ -86,6 +92,7 @@ impl Tree {
8692
rv,
8793
Tree {
8894
inner: tree,
95+
treeseq: ts,
8996
current_tree: 0,
9097
advanced: false,
9198
api
@@ -94,8 +101,8 @@ impl Tree {
94101
}
95102
}
96103

97-
impl streaming_iterator::StreamingIterator for Tree {
98-
type Item = Tree;
104+
impl<'ts> streaming_iterator::StreamingIterator for Tree<'ts> {
105+
type Item = Tree<'ts>;
99106
fn advance(&mut self) {
100107
let rv = if self.current_tree == 0 {
101108
unsafe { ll_bindings::tsk_tree_first(self.as_mut_ptr()) }
@@ -113,15 +120,15 @@ impl streaming_iterator::StreamingIterator for Tree {
113120
}
114121
}
115122

116-
fn get(&self) -> Option<&Tree> {
123+
fn get(&self) -> Option<&Self::Item> {
117124
match self.advanced {
118125
true => Some(self),
119126
false => None,
120127
}
121128
}
122129
}
123130

124-
impl streaming_iterator::DoubleEndedStreamingIterator for Tree {
131+
impl<'ts> streaming_iterator::DoubleEndedStreamingIterator for Tree<'ts> {
125132
fn advance_back(&mut self) {
126133
let rv = if self.current_tree == 0 {
127134
unsafe { ll_bindings::tsk_tree_last(self.as_mut_ptr()) }
@@ -334,6 +341,21 @@ impl TreeSequence {
334341
/// }
335342
/// ```
336343
///
344+
/// ## Coupled liftimes
345+
///
346+
/// A `Tree`'s lifetime is tied to that of its tree sequence:
347+
///
348+
/// ```{compile_fail}
349+
/// # use streaming_iterator::StreamingIterator;
350+
/// # use streaming_iterator::DoubleEndedStreamingIterator;
351+
/// # let mut tables = tskit::TableCollection::new(1000.).unwrap();
352+
/// # tables.build_index();
353+
/// let tree_sequence = tables.tree_sequence(tskit::TreeSequenceFlags::default()).unwrap();
354+
/// let mut tree_iterator = tree_sequence.tree_iterator(tskit::TreeFlags::default()).unwrap();
355+
/// drop(tree_sequence);
356+
/// while let Some(tree) = tree_iterator.next() { // compile fail.
357+
/// }
358+
/// ```
337359
/// # Warning
338360
///
339361
/// The following code results in an infinite loop.

0 commit comments

Comments
 (0)