1

Consider the following invalid Rust code. There is one struct Foo that contains a reference to a second struct Bar:

struct Foo<'a> {
    bar: &'a Bar,
}

impl<'a> Foo<'a> {
    fn new(bar: &'a Bar) -> Foo<'a> {
        Foo { bar }
    }
}

struct Bar {
    value: String,
}

impl Bar {
    fn empty() -> Bar {
        Bar {
            value: String::from("***"),
        }
    }
}

fn main() {
    let foo = Foo::new(&Bar::empty());
    println!("{}", foo.bar.value);
}

The compiler does not like this:

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:24:25
   |
24 |     let foo = Foo::new(&Bar::empty());
   |                         ^^^^^^^^^^^^ - temporary value is freed at the end of this statement
   |                         |
   |                         creates a temporary which is freed while still in use
25 |     println!("{}", foo.bar.value);
   |                    ------------- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

I can make it work by doing what the compiler says - using a let binding:

fn main() {
    let bar = &Bar::empty();
    let foo = Foo::new(bar);
    println!("{}", foo.bar.value);
}

However, suddenly I need two lines for something as trivial as instantiating my Foo. Is there any simple way to fix this in a one-liner?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Anders
  • 8,307
  • 9
  • 56
  • 88
  • 1
    Who is supposed to own the `Bar` in `Foo::new(&Bar::empty())`? The `Foo` struct doesn't own it, it only references it. Are you sure that you wanted a `&'a Bar`, and not just `Bar`, or something like `Box`, or `Rc`? Currently, your `Foo` has no raison d'être without a `Bar`, therefore you cannot instantiate a `Foo` without having a `Bar` in scope. – Andrey Tyukin Jan 19 '19 at 09:01
  • @AndreyTyukin There will be a parent struct owning both the bar and the foos referencing it. So foos will be spawned from the parent struct with a reference to the bar. However, when I am writing tests I want to create a lot of foos with a throwaway dummy bar, and I find my code for doing that gets very convoluted. – Anders Jan 19 '19 at 09:03
  • 1
    Maybe it's noteworthy that both `let foo = Foo { bar: &Bar::empty() };` and `println!("{}", Foo::new(&Bar::empty()).bar.value);` work, because *"If you immediately assign the reference to a variable in a let statement (or make it part of some struct or array that is being immediately assigned), then Rust makes the [referenced] anonymous variable live as long as the variable the let initializes [...] Otherwise, the anonymous variable lives to the end of the enclosing statement"* (Blandy, Orendorff). Especially the second part seems useful for short `assert` statements. – Andrey Tyukin Jan 19 '19 at 09:27
  • 1
    @AndreyTyukin Thanks for the input. Unfortunately, I need to use the `new` constructor. And doing the construction in the same line of code as the struct is used is usually not feasible since it may be used in multiple assertions etc. So I guess I will have to use a separate `let ` statement to create the bar. – Anders Jan 19 '19 at 09:31
  • @AndreyTyukin By the way, I still think your comment qualifies as a good answer to my question so feel free to post it as such. – Anders Jan 19 '19 at 09:46
  • 1
    @Anders if this is sufficiently annoying, you could create a simple macro to have a shorter syntax. – Chronial Jan 19 '19 at 19:39

1 Answers1

1

No, there is no such syntax, other than what you have typed.

For the details of how long a temporary lives when you take a reference to it, see:


There will be a parent struct owning both the bar and the foos referencing it.

Good luck:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Now that NLL are stable, has anybody investigated whether it is a good idea to extend the lifetime of temporaries? – starblue Jan 20 '19 at 11:51
  • @starblue to be honest, I thought that [making the functions in question const](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=210bc4497f0ee6d9e01619fcb60517c9) would allow this to work, but it doesn't (yet?) – Shepmaster Jan 20 '19 at 16:17
  • @starblue ah, that was [deliberately restricted](https://github.com/rust-lang/rust/pull/53851), so it's possible it might work in the future. I think it's orthogonal to NLL though. – Shepmaster Jan 20 '19 at 17:01