0

I have a struct that wraps a value. For example:

struct PageSize {
   value: usize
}

I now want to pass this around, but use it as if it was its wrapped value. E.g.:

let home_size = PageSize { value: 100 }
let about_size = PageSize { value: 200 }

let average = (homesize + about_size) / 2

Arithmatic is but one example. Others could be length() or hour() on resp. a wrapped String, or wrapped DateTime.

Is there a trait, or pattern that lets me forward any method calls to its wrapped value? And that enables operators like arithmetic or logic on its wrapped number or boolean?

Additionally, I would like to take over the "behaviour" of the wrapped value. e.g. via a derive, macro or trait. Where currently I need to derive or impl Display, in order to display:

#[derive(Display)]
#[display(fmt = "{} bytes", value)]
struct PageSize {
   value: usize
}

println!("size: {}", PageSize { value: 100 });

I'd prefer to configure my struct so that it inherits this behaviour from the wrapped value. When the wrapped value can be Debug, PartialEq, Display etc etc, then I'd like my PageSize Struct to inherit this. Not sure if this is possible at all, though.

I'm pretty sure I've seen this mentioned in either a rust book or article, but I forgot the terms and names, and location. So I cannot find the concept. An unfortunate issue is that Wrapped and unwrap() and Boxed etc already have meaning in Rust, which makes searching harder.

berkes
  • 26,996
  • 27
  • 115
  • 206

2 Answers2

2

Yes, you can implement std::ops::Deref and std::ops::DerefMut, here is the docs for deref

Timmy
  • 64
  • 1
  • 3
  • The answer is so simple and obvious. Thanks! – berkes Sep 29 '22 at 12:55
  • 1
    @berkes Actually, this is an antipattern. See [this](https://stackoverflow.com/questions/45086595/is-it-considered-a-bad-practice-to-implement-deref-for-newtypes) or [this](https://rust-unofficial.github.io/patterns/anti_patterns/deref.html). TL;DR don't do it. – jthulhu Sep 29 '22 at 15:27
  • Using `Deref` you lose the context for your mathematical operations, this would let you do `*page_size + *flight_distance` for example. – kmdreko Sep 29 '22 at 15:54
0

It's called Newtype Pattern in Rust.

pub struct Bar(Foo);

Even better with derive_more:

use derive_more::{Add, Display, Div};

#[derive(Debug, PartialEq, Display, Add, Div)]
#[display(fmt = "{} bytes", .0)]
pub struct PageSize(usize);

fn main() {
    let avg = (PageSize(1) + PageSize(2)) / 2;
    println!("{}", avg);
}
Field
  • 389
  • 1
  • 4
  • As pointed out in a comment to @Timmy's answer, using deref to "get through" newtypes is an antipattern. – jthulhu Sep 29 '22 at 15:28
  • @BlackBeans I was aware of that argument but I think it really depends how you would like to define your type. I hold the a similar opinion as [this](https://stackoverflow.com/questions/45086595/is-it-considered-a-bad-practice-to-implement-deref-for-newtypes) in his conclusion. And I agree to remove the `Deref` part of my answer. – Field Sep 29 '22 at 15:44
  • @BlackBeans I would like to add that I think "get through" newtypes should be at least ok for wrapped number as long as not wrapping arbritray self defined type. In an intuitionistic logic way, `PageSize` is a number in nature, PageSize ⊂ usize ⊂ Z+, it should be used transparently as `usize`. – Field Sep 29 '22 at 16:16
  • in Rust, you want to create newtypes when you *don't* want total transparency, which, btw, you don't obtain by implementing `Deref`, because deref coercion is only done under specific circumstances. If you want transparency for certain aspects, you have to implement them yourself (not necessarily by hand, you can use a macro). I am currently working on a crate to ease that, as, unfortunately, I'm not aware of any very powerful crate that does this kind of things for you. – jthulhu Sep 29 '22 at 16:23
  • @BlackBeans Yes, I agree it would be much better if there is another specific syntax/crate to allow well-defined transparency. Please let me know you have any progress on that project. – Field Sep 29 '22 at 16:35