Extracting a function which creates an instance in Rust

Viewed 71

A bit of a beginner Rust question - say I have the following code, which compiles:

trait XGetter {
    fn get_x(&self) -> i32;
}

struct Foo {
    x: i32
}
impl XGetter for Foo {
    fn get_x(&self) -> i32 {
        self.x
    }
}

struct Bar<'a>(&'a dyn XGetter);
impl<'a> XGetter for Bar<'a> {
    fn get_x(&self) -> i32 {
        self.0.get_x()
    }
}

fn baz() -> i32 {
    let foo = Foo { x: 42 };
    let bar = Bar(&foo);
    bar.get_x()
}

Let's say I want to extract out the creation of Bar, in order encapsulate the creation of the XGetter and Bar together, such that baz() now reads:

fn baz2() -> i32 {
    let bar = createBar(42);
    bar.get_x()
}

However, by implementing createBar below, I run a-fowl of the borrow checker:

fn createBar<'a>(x: i32) -> Bar<'a> {
    let foo = Foo { x };
    let bar = Bar(&foo);
//                ---- `foo` is borrowed here
    bar
//  ^^^ returns a value referencing data owned by the current function
}

How would one extract out a function createBar which doesn't break the borrowing rules?

2 Answers

The foo in createBar() dies when the function ends, so the bar you return would be pointing to invalid memory.

Given how you have written the call to createBar(42), it looks like you want Bar to own the Foo, so do that:

struct Bar(Box<dyn XGetter>);

impl XGetter for Bar {
    fn get_x(&self) -> i32 {
        self.0.get_x()
    }
}

fn createBar(x: i32) -> Bar {
    let foo = Box::new(Foo { x });
    let bar = Bar(foo);
    bar
}

You can not:

The signature struct Bar<'a>(&'a dyn XGetter); and createBar(i: 32) are incompatible. Because it means that in createBar you would have to instantiate an object implementing XGetter and that reference will not live outside of the scope of createBar.

fn createBar<'a>(x: i32) -> Bar<'a> {
    let foo = Foo { x };
    let bar = Bar(&foo);
//                ---- `foo` is borrowed here
    bar
//  ^^^ returns a value referencing data owned by the current function
}

^^^ That means that the variable foo will live just during createBar scope.

That said, you could use:

fn createBar(g: &dyn XGetter) -> Bar<'_> {
    Bar(g)
}

That way the reference will live outside of the scope of createBar.

Playground

As per the comments. If you want to abstract that, you need Bar to own Foo

struct Bar(Box<dyn XGetter>);


fn createBar(x: i32) -> Bar {
    let foo = Box::new(Foo { x });
    let bar = Bar(foo);
    bar
}
Related