Skip to content

Transmuting associated type of trait ref + data ref #577

@dhardy

Description

@dhardy

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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions