-
Notifications
You must be signed in to change notification settings - Fork 61
Open
Description
I haven't seen this pattern discussed:
pub trait Widget {
type Data;
fn as_node<'a>(&'a mut self, data: &'a Self::Data) -> Node<'a>;
fn child_node<'n>(&'n mut self, data: &'n Self::Data, index: usize) -> Option<Node<'n>>;
// ... other methods which may take `&Self::Data` but do not use this type in any other way
}
pub struct Node<'a>(
&'a mut dyn Widget<Data = ()>,
&'a (),
);
impl<'a> Node<'a> {
/// Construct
#[inline(always)]
pub fn new<T>(widget: &'a mut dyn Widget<Data = T>, data: &'a T) -> Self {
// Safety: since the vtable for dyn Widget<Data = T> only uses T as &T
// and T: Sized, the vtable should be equivalent for all T.
// We ensure here that the type of `data` matches that used by `widget`.
// NOTE: This makes assumptions beyond Rust's specification.
use std::mem::transmute;
unsafe { Node(transmute(widget), transmute(data)) }
}
pub fn get_child(&mut self, index: usize) -> Option<Node<'_>> {
self.0.child_node(self.1, index)
}
}
I believe this falls into the valley of "not UB but also not specified sound behaviour"? It makes some assumptions about the vtable and about &()
not being optimised out, but does appear to work.
Note: type Widget::Data
does not have a lifetime bound, but this can be changed if recommended — I don't think this changes anything however.
Full code (including a safe implementation using boxing) can be found here: https://github.com/kas-gui/kas/blob/master/crates/kas-core/src/core/node.rs
Metadata
Metadata
Assignees
Labels
No labels