Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Deref on a struct that owns a boxed trait

Tags:

rust

I'd like to implement Deref and DefrefMut on a struct that owns a boxed trait, e.g.:

use std::ops::{Deref, DerefMut};

trait Quack {
    fn quack(&self);
}

struct QuackWrap {
    value: Box<Quack>
}

impl Deref for QuackWrap {
    type Target = Box<Quack>;

    fn deref<'a>(&'a self) -> &'a Box<Quack> {
        &self.value
    }
}

impl DerefMut for QuackWrap {
    fn deref_mut<'a>(&'a mut self) -> &'a mut Box<Quack> {
        &mut self.value
    }
}

This fails to compile with the following error:

src/main.rs:14:5: 16:6 error: method `deref` has an incompatible type for trait: expected bound lifetime parameter 'a, found concrete lifetime [E0053]
src/main.rs:14     fn deref<'a>(&'a self) -> &'a Box<Quack> {
src/main.rs:15         &self.value
src/main.rs:16     }
src/main.rs:20:5: 22:6 error: method `deref_mut` has an incompatible type for trait: expected bound lifetime parameter 'a, found concrete lifetime [E0053]
src/main.rs:20     fn deref_mut<'a>(&'a mut self) -> &'a mut Box<Quack> {
src/main.rs:21         &mut self.value
src/main.rs:22     }

If I replace Box<Quack> with Box<String> (or a similar type), it works. The problem is that Quack is a trait. But I'm not sure why that generated the error message it did. Any ideas?

My question is similar to another SO question, but not quite the same. In that question, the struct has a type parameter with the trait as a constraint. Whereas in my question, there is no type parameter.

I don't want to confuse the issues, but there's a good reason that I need Box<Quack> in my application. I.e. I can't replace Quack with a type parameter. In case you care, the reason is discussed further in another SO question.

like image 527
rlkw1024 Avatar asked Jun 05 '15 18:06

rlkw1024


2 Answers

When in doubt, add more lifetime annotations:

use std::ops::{Deref, DerefMut};

trait Quack {
    fn quack(&self);
}

struct QuackWrap<'b> {
    value: Box<Quack + 'b>
}

impl<'b> Deref for QuackWrap<'b>{
    type Target = Box<Quack + 'b>;

    fn deref<'a>(&'a self) -> &'a Box<Quack + 'b> {
        &self.value
    }
}

impl<'b> DerefMut for QuackWrap<'b> {
    fn deref_mut<'a>(&'a mut self) -> &'a mut Box<Quack + 'b> {
        &mut self.value
    }
}
like image 116
Brian Campbell Avatar answered Oct 27 '22 19:10

Brian Campbell


Based on Brian's answer and Shepmaster's explanation, I updated my code as follow. I also simplified the QuackWrap struct. (That wasn't strictly necessary, but it's arguably better style than what I was doing before.)

use std::ops::{Deref, DerefMut};

trait Quack {
    fn quack(&self);
}

struct QuackWrap(Box<Quack>);

impl Deref for QuackWrap {
    type Target = Box<Quack + 'static>;

    fn deref<'a>(&'a self) -> &'a Box<Quack + 'static> {
        let QuackWrap(ref v) = *self;
        v
    }
}

impl DerefMut for QuackWrap {
    fn deref_mut<'a>(&'a mut self) -> &'a mut Box<Quack + 'static> {
        let QuackWrap(ref mut v) = *self;
        v
    }
}

There may be a more concise way to destructure QuackWrap in the deref and deref_mut implementations. Some of those more obscure syntax rules elude me. But for now this is fine.

like image 39
rlkw1024 Avatar answered Oct 27 '22 18:10

rlkw1024