Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatically implement traits of enclosed type for Rust newtypes (tuple structs with one field)

In Rust, tuple structs with only one field can be created like the following:

struct Centimeters(i32); 

I want to do basic arithmetic with Centimeters without extracting their "inner" values every time with pattern matching, and without implementing the Add, Sub, ... traits and overloading operators.

What I want to do is:

let a = Centimeters(100); let b = Centimeters(200); assert_eq!(a + a, b); 
like image 246
anonymous_user_13 Avatar asked Jul 18 '14 18:07

anonymous_user_13


People also ask

What is the rust equivalent of Haskell's generalizednewtypederiving?

Rust doesn't have an equivalent to the Haskell's GHC extension GeneralizedNewtypeDeriving which allows deriving on wrapper types to automatically implement any type class/trait that the wrapped type implements (and with the current set-up of Rust's # [derive] as a simple AST transformation, implementing it like Haskell is essentially impossible.)

What is the use of struct attribute in rust?

This attribute allows us to print out structs for easier debugging. Attributes act as directives to the compiler to write out the boilerplate. There are several other built in derive attributes in Rust that we can use to allow the compiler to implement certain traits for us:

What are rust traits and how are they used?

Rust uses a feature called traits, which define a bundle of functions for structs to implement. One benefit of traits is you can use them for typing. You can create functions that can be used by any structs that implement the same trait. Essentially, you can build methods into structs as long as you implement the right trait.

How do you create a custom data type in rust?

We can create custom data types in Rust using struct, trait, and enum. A struct is a composite data type that groups variables in a memory block. These variables are then accessible via the struct’s instance – also referred to as an object in OOP languages.


2 Answers

is there a way to do it without extracting their "inner" values every time with pattern matching, and without implementing the Add, Sub, ... traits and overloading operators?

No, the only way is to implement the traits manually. Rust doesn't have an equivalent to the Haskell's GHC extension GeneralizedNewtypeDeriving which allows deriving on wrapper types to automatically implement any type class/trait that the wrapped type implements (and with the current set-up of Rust's #[derive] as a simple AST transformation, implementing it like Haskell is essentially impossible.)

To abbreviate the process, you could use a macro:

use std::ops::{Add, Sub};  macro_rules! obvious_impl {     (impl $trait_: ident for $type_: ident { fn $method: ident }) => {         impl $trait_<$type_> for $type_ {             type Output = $type_;              fn $method(self, $type_(b): $type_) -> $type_ {                 let $type_(a) = self;                 $type_(a.$method(&b))             }         }     } }  #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)] pub struct Centimeters(i32);  obvious_impl! { impl Add for Centimeters { fn add } } obvious_impl! { impl Sub for Centimeters { fn sub } }  #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)] pub struct Inches(i32);  obvious_impl! { impl Add for Inches { fn add } } obvious_impl! { impl Sub for Inches { fn sub } }  fn main() {     let a = Centimeters(100);     let b = Centimeters(200);     let c = Inches(10);     let d = Inches(20);     println!("{:?} {:?}", a + b, c + d); // Centimeters(300) Inches(30)     // error:     // a + c; } 

playpen

I emulated the normal impl syntax in the macro to make it obvious what is happening just by looking at the macro invocation (i.e. reducing the need to look at the macro definition), and also to maintain Rust's natural searchability: if you're looking for traits on Centimeters just grep for for Centimeters and you'll find these macro invocations along with the normal impls.

If you are accessing the contents of the Centimeters type a lot, you could consider using a proper struct with a field to define the wrapper:

struct Centimeters { amt: i32 } 

This allows you to write self.amt instead of having to do the pattern matching. You can also define a function like fn cm(x: i32) -> Centimeters { Centimeters { amt: x } }, called like cm(100), to avoid the verbosity of constructing a full struct.

You can also access the inner values of a tuple struct using the .0, .1 syntax.

like image 173
huon Avatar answered Sep 21 '22 21:09

huon


I made the derive_more crate for this problem. It can derive lots of traits for structs of which the elements implement them.

You need to add derive_more to your Cargo.toml. Then you can write:

#[macro_use] extern crate derive_more;  #[derive(Clone, Copy, Debug, PartialEq, Eq, Add)] struct Centimeters(i32);  fn main() {     let a = Centimeters(100);     let b = Centimeters(200);     assert_eq!(a + a, b); } 
like image 45
JelteF Avatar answered Sep 21 '22 21:09

JelteF