Skip to content

refactor: use GATs in Node and Property traits#29

Open
m4tx wants to merge 7 commits into
mainfrom
node-gats
Open

refactor: use GATs in Node and Property traits#29
m4tx wants to merge 7 commits into
mainfrom
node-gats

Conversation

@m4tx
Copy link
Copy Markdown
Collaborator

@m4tx m4tx commented Feb 24, 2026

This changes the Node and Property traits so that instead of requiring external lifetimes, the lifetimes are defined by the trait implementations themselves. This is mainly useful for unifying the Fdt APIs (which borrow the data) and DeviceTree APIs (which own the data).

Furthermore, this will be useful when we introduce mutable Fdt in-place APIs, since &mut self methods cannot return non-self-borrowed data. With the old version this would require implementing the mutating traits for &T rather than for T as we did with DeviceTree trait impls.

To make things easier for users, specific types are used everywhere in the implementations of the traits, so that rustdoc clearly displays the actual return types.

The main downsides is increased complexity of the traits and increased code duplication in some places.

This changes the `Node` and `Property` traits so that instead of
requiring external lifetimes, the lifetimes are defined by the trait
implementations themselves. This is mainly useful for unifying the `Fdt`
APIs (which borrow the data) and `DeviceTree` APIs (which own the data).

Furthermore, this will be useful when we introduce mutable `Fdt`
in-place APIs, since `&mut self` methods cannot return non-self-borrowed
data. With the old version this would require implementing the mutating
traits for `&T` rather than for `T` as we did with `DeviceTree` trait
impls.

The main downsides is increased complexity of the traits.
@m4tx m4tx requested a review from qwandor February 24, 2026 17:32
Comment thread src/fdt/node.rs Outdated
/// An iterator over the children of a device tree node.
enum FdtChildIter<'a> {
#[derive(Debug, Clone)]
pub struct FdtChildIter<'a>(FdtChildIterInner<'a>);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why do you need this extra wrapper type around the iterator?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The reason is that because now it appears as a type in a trait implementation, it needs to be public. I didn't want to expose the internals of the implementation though (i.e. iterator enum variants) so that we don't overpromise in the public API.

However, a better idea is probably to make it unnameable type instead by putting it in a private module, so that's what I'll do instead.

Comment thread src/fdt/node.rs
Comment thread src/model/node.rs

impl<'a> Node<'a> for &'a DeviceTreeNode {
type Property = &'a DeviceTreeProperty;
impl Node for DeviceTreeNode {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why does the trait need to be implemented both for DeviceTreeNode and &DeviceTreeNode?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The owned version is mostly provided for convenience, so that you don't have to wrap an owned node in a reference to call methods on it (node.child("foo") instead of (&node).child("foo").

Comment thread src/model/property.rs Outdated
Comment thread src/model/property.rs Outdated
fields_cells: [usize; N],
) -> Result<crate::values::PropEncodedArrayIterator<'a, N>, PropertyError> {
crate::values::PropEncodedArrayIterator::new(&self.value, fields_cells)
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

There's more code duplication here too, can we avoid it somehow?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We could move this to a macro at least.

Comment thread src/standard/memory.rs
Ok(Memory { node })
}

/// Returns the `/reserved-memory/*` nodes, if any.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is no longer true, it now returns the node itself rather than its children.

Comment thread src/standard/memory.rs Outdated

/// Typed wrapper for a `/reserved-memory` node.
#[derive(Clone, Copy, Debug)]
pub struct ReservedMemoryNode<N> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is this necessary? As far as I understand the /reserved-memory node won't have any properties of its own, only its children are relevant, so why do we need a type for the node itself?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

When writing the initial version of the code, I needed it because of some lifetime issues. Now it indeed is not needed (I guess I made some lifetime-related changes in the meantime that allowed this), so I just restored the previous version of the code - thanks for catching this!

Comment thread src/lib.rs Outdated
Comment thread src/lib.rs Outdated
@qwandor
Copy link
Copy Markdown
Collaborator

qwandor commented Apr 22, 2026

Please also fix the clippy failure.

@m4tx m4tx requested a review from qwandor May 18, 2026 14:31
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.

2 participants