Skip to content

Ensure &Node<T> is not removed - #198

Open
russellbanks wants to merge 3 commits into
saschagrunert:mainfrom
russellbanks:validate-nodes
Open

Ensure &Node<T> is not removed#198
russellbanks wants to merge 3 commits into
saschagrunert:mainfrom
russellbanks:validate-nodes

Conversation

@russellbanks

Copy link
Copy Markdown
Contributor

This pull request modifies the design of the API so that an &Node<T> to a consumer will never be a removed node.

Resolves #195

@codecov

codecov Bot commented May 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.41284% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.7%. Comparing base (44ec276) to head (22a15de).
⚠️ Report is 16 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##            main    #198     +/-   ##
=======================================
- Coverage   97.5%   96.7%   -0.8%     
=======================================
  Files         10      10             
  Lines       1127    1216     +89     
=======================================
+ Hits        1099    1177     +78     
- Misses        28      39     +11     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@saschagrunert saschagrunert left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good API improvement that addresses the inconsistency in #195. A few suggestions below.

Comment thread indextree/src/id.rs Outdated
Comment thread indextree/src/traverse.rs
Comment thread indextree/src/id.rs Outdated
Comment thread indextree/src/id.rs Outdated
Co-Authored-By: Sascha Grunert <sgrunert@redhat.com>
@russellbanks

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review @saschagrunert!

@saschagrunert saschagrunert left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second pass review, five inline comments.

Comment thread indextree/src/relations.rs
Comment thread indextree/src/node.rs
Comment thread indextree/src/debug_pretty_print.rs Outdated
Comment thread indextree/src/arena.rs
Comment thread indextree/tests/lib.rs
Comment thread indextree/src/node.rs
@@ -42,28 +42,20 @@ pub struct Node<T> {

impl<T> Node<T> {
/// Returns a reference to the node data.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The # Panics doc section was removed, but this method still panics when called on a freed node. Since the goal of this PR is that users never get a &Node<T> to a removed node, the panic should be unreachable in practice, but it could still be hit if someone holds a &Node<T> across a mutation that frees it.

Suggestion: either keep the # Panics section so the contract is discoverable, or change the return type to Option<&T> to make it truly safe. Same applies to get_mut() below.

Comment thread indextree/src/arena.rs
pub struct Arena<T> {
nodes: Vec<Node<T>>,
pub(crate) nodes: Vec<Node<T>>,
first_free_slot: Option<usize>,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Widening visibility to pub(crate) gives every module direct access to the raw vec, bypassing the removed-node filtering this PR introduces. Internal code can accidentally index a removed node without the compiler catching it.

Consider a pub(crate) fn get_node_unchecked(&self, id: NodeId) -> &Node<T> helper instead. That documents the intent ("I know this might be removed") while keeping the field private.

Comment thread indextree/src/arena.rs
#[inline]
pub fn is_empty(&self) -> bool {
self.count() == 0
self.nodes.is_empty()

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_empty() now checks the backing storage while count() only counts live nodes. After removing all nodes, count() == 0 is true but is_empty() is false. This inconsistency will likely surprise users.

Either align is_empty() with count() (e.g. self.iter().next().is_none()), or document that is_empty tracks storage occupancy, not live node count.

traverser.next();
{
let data = self.arena[*self.id].get();
if let Some(data) = self.arena.get(*self.id).map(Node::get) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the root node is removed, this if let silently produces empty output with no indication anything was wrong. Could be confusing for someone debugging a tree.

Consider returning Err or printing a placeholder like <removed> so the caller knows the root was invalid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API node validation inconsistency

2 participants