dextrose
dextrose

Reputation: 23

How to wrap a struct in Rust?

For a project I'm using a library in Rust which has the following structs & traits:

struct Server<C> {
    c: C,
}

impl<C: Customization> Server<C> {
    type R<'s> = ResultImpl<'s, C> where Self: 's;

    fn query<'s>(&mut self) -> Self::R<'_> {
        ResultImpl(self)
    }
}

trait Customization {}

struct CustomizationImpl<'c> {
    reference: &'c Foo,
}

struct Foo {}

impl Customization for CustomizationImpl<'_> {}

trait Result {}

struct ResultImpl<'s, C: Customization>(&'s mut Server<C>);

impl<'s, C: Customization> Result for ResultImpl<'s, C> {}

I know, it seems a bit odd that a Result holds a reference to a Server, but let's assume we can't change the code.

Now I want a wrapper around the Server struct. What I tried was the following:

struct Wrapper<'w> {
    server: Server<CustomizationImpl<'w>>,
}

impl<'w> Wrapper<'w> {
    fn query(&'w mut self) -> ResultImpl<'_, CustomizationImpl> {
        self.server.query()
    }
}

But then, I can't call query() more than once on Wrapper.

//compiles
let mut server = Server {
    c: CustomizationImpl { reference: &Foo {} }
};
server.query();
server.query();
    
//does not compile
let mut wrapper = Wrapper {
    server: Server { c: CustomizationImpl { reference: &Foo {} } },
};
wrapper.query();
wrapper.query();

With error error[E0499]: cannot borrow wrapper as mutable more than once at a time. You can also find my code at https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=eebfd28e33f5354ffe9b46ff121f5f89

Is there any way to wrap the Server struct?

Upvotes: 1

Views: 1270

Answers (1)

rodrigo
rodrigo

Reputation: 98398

Your Wrapper lifetime declarations are wrong. Your code is actually equivalent to this one:

impl<'w> Wrapper<'w> {
    fn query(&'w mut self) -> ResultImpl<'w, CustomizationImpl<'w>> {
        self.server.query()
    }
}

But the two lifetimes in the output type should be unrelated. And creating a value of a type such as &'x Type<'x> is known to cause issues such as yours.

The solution is something like this, having the lifetime of self and that of the generic separated:

impl<'w> Wrapper<'w> {
    fn query<'s>(&'s mut self) -> ResultImpl<'s, CustomizationImpl<'w>> {
        self.server.query()
    }
}

Upvotes: 1

Related Questions