Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Post History

60%
+1 −0
Q&A Recursive traversal of composite tree of mutable "trait objects"?

Here's the solution I ended up with a while back to traverse the (sub-)tree, starting at the given root component. In short, the model's field, components, is used directly and small unsafe blocks ...

posted 25d ago by ghost-in-the-zsh‭  ·  edited 24d ago by ghost-in-the-zsh‭

Answer
#3: Post edited by user avatar ghost-in-the-zsh‭ · 2024-10-24T23:28:03Z (24 days ago)
Fix typo
  • Here's the solution I ended up with a while back to traverse the (sub-)tree, starting at the given root component. In short, the models's field, `components`, is used directly and small `unsafe` blocks are used to push the root's children into the vector-backed queue that's currently being traversed.
  • While a full working example can be found at [this playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e0e914ae50d38b8b6a481e0bfd5070cc), the minimal/core part of it is below:
  • ```rust
  • impl Component for Model {
  • fn children(&self) -> Vec<&dyn Component> {
  • let mut v: Vec<&dyn Component> = self.components
  • .iter()
  • .map(|c| c.deref())
  • .collect();
  • let q = &mut v as *mut Vec<&dyn Component>;
  • v.iter().for_each(|c| unsafe {
  • (*q).append(&mut c.children())
  • });
  • v
  • }
  • fn children_mut(&mut self) -> Vec<&mut dyn Component> {
  • let mut v: Vec<&mut dyn Component> = self.components
  • .iter_mut()
  • .map(|c| c.deref_mut())
  • .collect();
  • let q = &mut v as *mut Vec<&mut dyn Component>;
  • unsafe {
  • (*q).iter_mut().for_each(|c| {
  • (*q).append(&mut c.children_mut())
  • })
  • }
  • v
  • }
  • }
  • ```
  • The caveat/con here is that, under the *experimental* "Stacked Borrows" memory model in the *nightly* toolchain, Miri says the above causes UB inside the `Vec` implementation:
  • ```text
  • error: Undefined Behavior: deallocating while item [SharedReadOnly for <9045>] is strongly protected
  • --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:776:13
  • |
  • 776 | alloc.grow(ptr, old_layout, new_layout)
  • | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [SharedReadOnly for <9045>] is strongly protected
  • |
  • = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
  • = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
  • = note: BACKTRACE:
  • = note: inside `alloc::raw_vec::finish_grow::<std::alloc::Global>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:776:13: 776:52
  • = note: inside `alloc::raw_vec::RawVecInner::grow_amortized` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:661:19: 661:93
  • = note: inside `alloc::raw_vec::RawVecInner::<A>::reserve::do_reserve_and_handle::<std::alloc::Global>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:554:31: 554:79
  • note: inside closure
  • ```
  • In short, Miri says the `Vec` implementation runs into UB while allocating memory for the vector, likely b/c the `Vec` is being modified while being traversed.
  • I've not (yet?) been successful in refactoring the above to remove the `unsafe` blocks. Perhaps some method type signature changes I'm not (currently) aware of would be required.
  • If you have specific suggestions, please post them in a comment.
  • Here's the solution I ended up with a while back to traverse the (sub-)tree, starting at the given root component. In short, the model's field, `components`, is used directly and small `unsafe` blocks are used to push the root's children into the vector-backed queue that's currently being traversed.
  • While a full working example can be found at [this playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e0e914ae50d38b8b6a481e0bfd5070cc), the minimal/core part of it is below:
  • ```rust
  • impl Component for Model {
  • fn children(&self) -> Vec<&dyn Component> {
  • let mut v: Vec<&dyn Component> = self.components
  • .iter()
  • .map(|c| c.deref())
  • .collect();
  • let q = &mut v as *mut Vec<&dyn Component>;
  • v.iter().for_each(|c| unsafe {
  • (*q).append(&mut c.children())
  • });
  • v
  • }
  • fn children_mut(&mut self) -> Vec<&mut dyn Component> {
  • let mut v: Vec<&mut dyn Component> = self.components
  • .iter_mut()
  • .map(|c| c.deref_mut())
  • .collect();
  • let q = &mut v as *mut Vec<&mut dyn Component>;
  • unsafe {
  • (*q).iter_mut().for_each(|c| {
  • (*q).append(&mut c.children_mut())
  • })
  • }
  • v
  • }
  • }
  • ```
  • The caveat/con here is that, under the *experimental* "Stacked Borrows" memory model in the *nightly* toolchain, Miri says the above causes UB inside the `Vec` implementation:
  • ```text
  • error: Undefined Behavior: deallocating while item [SharedReadOnly for <9045>] is strongly protected
  • --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:776:13
  • |
  • 776 | alloc.grow(ptr, old_layout, new_layout)
  • | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [SharedReadOnly for <9045>] is strongly protected
  • |
  • = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
  • = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
  • = note: BACKTRACE:
  • = note: inside `alloc::raw_vec::finish_grow::<std::alloc::Global>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:776:13: 776:52
  • = note: inside `alloc::raw_vec::RawVecInner::grow_amortized` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:661:19: 661:93
  • = note: inside `alloc::raw_vec::RawVecInner::<A>::reserve::do_reserve_and_handle::<std::alloc::Global>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:554:31: 554:79
  • note: inside closure
  • ```
  • In short, Miri says the `Vec` implementation runs into UB while allocating memory for the vector, likely b/c the `Vec` is being modified while being traversed.
  • I've not (yet?) been successful in refactoring the above to remove the `unsafe` blocks. Perhaps some method type signature changes I'm not (currently) aware of would be required.
  • If you have specific suggestions, please post them in a comment.
#2: Post edited by user avatar ghost-in-the-zsh‭ · 2024-10-24T23:25:35Z (24 days ago)
Improve wording
  • Here's the solution I ended up with a while back to traverse the (sub-)tree, starting at the given root component. In short, the models's field, `components`, is used directly and small `unsafe` blocks are used to push the root's children into the vector-backed queue that's currently being traversed.
  • While a full working example can be found at [this playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e0e914ae50d38b8b6a481e0bfd5070cc), the minimal/core part of it is below:
  • ```rust
  • impl Component for Model {
  • fn children(&self) -> Vec<&dyn Component> {
  • let mut v: Vec<&dyn Component> = self.components
  • .iter()
  • .map(|c| c.deref())
  • .collect();
  • let q = &mut v as *mut Vec<&dyn Component>;
  • v.iter().for_each(|c| unsafe {
  • (*q).append(&mut c.children())
  • });
  • v
  • }
  • fn children_mut(&mut self) -> Vec<&mut dyn Component> {
  • let mut v: Vec<&mut dyn Component> = self.components
  • .iter_mut()
  • .map(|c| c.deref_mut())
  • .collect();
  • let q = &mut v as *mut Vec<&mut dyn Component>;
  • unsafe {
  • (*q).iter_mut().for_each(|c| {
  • (*q).append(&mut c.children_mut())
  • })
  • }
  • v
  • }
  • }
  • ```
  • The caveat/con here is that, under the *experimental* "Stacked Borrows" memory model in the *nightly* toolchain, Miri says the above causes UB:
  • ```text
  • error: Undefined Behavior: deallocating while item [SharedReadOnly for <9045>] is strongly protected
  • --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:776:13
  • |
  • 776 | alloc.grow(ptr, old_layout, new_layout)
  • | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [SharedReadOnly for <9045>] is strongly protected
  • |
  • = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
  • = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
  • = note: BACKTRACE:
  • = note: inside `alloc::raw_vec::finish_grow::<std::alloc::Global>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:776:13: 776:52
  • = note: inside `alloc::raw_vec::RawVecInner::grow_amortized` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:661:19: 661:93
  • = note: inside `alloc::raw_vec::RawVecInner::<A>::reserve::do_reserve_and_handle::<std::alloc::Global>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:554:31: 554:79
  • note: inside closure
  • ```
  • In short, Miri says the `Vec` implementation runs into UB while allocating memory for the vector, b/c it's being modified while being traversed. I've not (yet?) been successful in refactoring the above to remove the `unsafe` blocks. Perhaps some method type signature changes I'm not (currently) aware of would be required.
  • If you have specific suggestions, please post a comment.
  • Here's the solution I ended up with a while back to traverse the (sub-)tree, starting at the given root component. In short, the models's field, `components`, is used directly and small `unsafe` blocks are used to push the root's children into the vector-backed queue that's currently being traversed.
  • While a full working example can be found at [this playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e0e914ae50d38b8b6a481e0bfd5070cc), the minimal/core part of it is below:
  • ```rust
  • impl Component for Model {
  • fn children(&self) -> Vec<&dyn Component> {
  • let mut v: Vec<&dyn Component> = self.components
  • .iter()
  • .map(|c| c.deref())
  • .collect();
  • let q = &mut v as *mut Vec<&dyn Component>;
  • v.iter().for_each(|c| unsafe {
  • (*q).append(&mut c.children())
  • });
  • v
  • }
  • fn children_mut(&mut self) -> Vec<&mut dyn Component> {
  • let mut v: Vec<&mut dyn Component> = self.components
  • .iter_mut()
  • .map(|c| c.deref_mut())
  • .collect();
  • let q = &mut v as *mut Vec<&mut dyn Component>;
  • unsafe {
  • (*q).iter_mut().for_each(|c| {
  • (*q).append(&mut c.children_mut())
  • })
  • }
  • v
  • }
  • }
  • ```
  • The caveat/con here is that, under the *experimental* "Stacked Borrows" memory model in the *nightly* toolchain, Miri says the above causes UB inside the `Vec` implementation:
  • ```text
  • error: Undefined Behavior: deallocating while item [SharedReadOnly for <9045>] is strongly protected
  • --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:776:13
  • |
  • 776 | alloc.grow(ptr, old_layout, new_layout)
  • | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [SharedReadOnly for <9045>] is strongly protected
  • |
  • = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
  • = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
  • = note: BACKTRACE:
  • = note: inside `alloc::raw_vec::finish_grow::<std::alloc::Global>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:776:13: 776:52
  • = note: inside `alloc::raw_vec::RawVecInner::grow_amortized` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:661:19: 661:93
  • = note: inside `alloc::raw_vec::RawVecInner::<A>::reserve::do_reserve_and_handle::<std::alloc::Global>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:554:31: 554:79
  • note: inside closure
  • ```
  • In short, Miri says the `Vec` implementation runs into UB while allocating memory for the vector, likely b/c the `Vec` is being modified while being traversed.
  • I've not (yet?) been successful in refactoring the above to remove the `unsafe` blocks. Perhaps some method type signature changes I'm not (currently) aware of would be required.
  • If you have specific suggestions, please post them in a comment.
#1: Initial revision by user avatar ghost-in-the-zsh‭ · 2024-10-24T02:02:21Z (25 days ago)
Here's the solution I ended up with a while back to traverse the (sub-)tree, starting at the given root component. In short, the models's field, `components`, is used directly and small `unsafe` blocks are used to push the root's children into the vector-backed queue that's currently being traversed.

While a full working example can be found at [this playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e0e914ae50d38b8b6a481e0bfd5070cc), the minimal/core part of it is below:

```rust
impl Component for Model {
    fn children(&self) -> Vec<&dyn Component> {
        let mut v: Vec<&dyn Component> = self.components
            .iter()
            .map(|c| c.deref())
            .collect();
        let q = &mut v as *mut Vec<&dyn Component>;
        v.iter().for_each(|c| unsafe {
            (*q).append(&mut c.children())
        });
        v
    }

    fn children_mut(&mut self) -> Vec<&mut dyn Component> {
        let mut v: Vec<&mut dyn Component> = self.components
            .iter_mut()
            .map(|c| c.deref_mut())
            .collect();
        let q = &mut v as *mut Vec<&mut dyn Component>;
        unsafe {
            (*q).iter_mut().for_each(|c| {
                (*q).append(&mut c.children_mut())
            })
        }
        v
    }
}
```

The caveat/con here is that, under the *experimental* "Stacked Borrows" memory model in the *nightly* toolchain, Miri says the above causes UB:

```text
error: Undefined Behavior: deallocating while item [SharedReadOnly for <9045>] is strongly protected
   --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:776:13
    |
776 |             alloc.grow(ptr, old_layout, new_layout)
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [SharedReadOnly for <9045>] is strongly protected
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
    = note: BACKTRACE:
    = note: inside `alloc::raw_vec::finish_grow::<std::alloc::Global>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:776:13: 776:52
    = note: inside `alloc::raw_vec::RawVecInner::grow_amortized` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:661:19: 661:93
    = note: inside `alloc::raw_vec::RawVecInner::<A>::reserve::do_reserve_and_handle::<std::alloc::Global>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:554:31: 554:79
note: inside closure
```

In short, Miri says the `Vec` implementation runs into UB while allocating memory for the vector, b/c it's being modified while being traversed. I've not (yet?) been successful in refactoring the above to remove the `unsafe` blocks. Perhaps some method type signature changes I'm not (currently) aware of would be required.

If you have specific suggestions, please post a comment.