rustfieldwrappernewtype

When wrapping a type with Rust's newtype, how can inner fields be exposed or themselves wrapped?


As a newcomer to Rust, it seems that one of the most useful parts of the language is the struct, in particular the fact that it has named fields. This seems pretty core to the language as a whole.

I wish to wrap a third-party type called glam::f64::DVec4 as my own type, Tuple, such that I can control what functions are available on my type, but delegate to glam's other functions as needed. From what I can glean from examples and docs, the way to go is to use a newtype:

pub struct Tuple(glam::f64::DVec4);

impl Tuple {
    pub fn new(x: f64, y: f64, z: f64, w: f64) -> Self {
        Self(glam::f64::DVec4 {x, y, z, w})
    }
}

// Factory function for ease of construction without referencing the type directly:
pub fn tuple(x: f64, y: f64, z: f64, w: f64) -> Tuple {
     Tuple::new(x, y, z, w)
}

However, whereas previously I would have been able to access the inner type's fields directly with .x or .y, I seem to lose this when I wrap it (unless I use Tuple.0.x I suppose, which seems ugly to me, and doesn't let me rename them, or perhaps intercept access to these fields via a function in my own code).

So I'm looking for some kind of "property" support (as one might have in, say, Python), that allows new fields to be created on the newtype Tuple, with whatever names I wish, and then map these to the original type's fields x, y, etc.

So in the end I'd have something like this (this does not compile):

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn default_tuple() {
        let a = tuple(1.0, 2.0, 3.0, 4.0);
        assert_eq!(a.x, 1.0);
        assert_eq!(a.y, 2.0);
        assert_eq!(a.z, 3.0);
        assert_eq!(a.w, 4.0);
    }

Is this possible, or will I need to add some kind of trait that provides x(), y(), ... access functions instead, and then assert_eq!(a.x(), 1.0)?

If creating or retaining fields is not possible, this seems to make newtypes omit the most useful thing about Rust's struct types - the named fields. Am I missing something?


Solution

  • The property syntax (.0 or .name) is only for actual properties of a struct. While it's somewhat restrictive, it does offer simplicity: if you see it, you know there is only one thing it can mean.

    Compared to other languages, it's relatively uncommon in rust to use property names for access. The main reason why rust allows defining methods with the same name as properties is to allow you to make getter methods for your properties, which is what you will find on most structs in the rust ecosystem. These would usually be methods on your type and not in traits. If you need those fields for a trait, then the trait implementation should call the type-specific methods.

    You can also implement AsRef<DVec4> and AsMut<DVec4>, which will make it easier for your code to be generic over Tuple and DVec4. You can also implement the comparison traits like PartialEq and conversion traits like From to make it easier to use Tuple and DVec4 around each other.