0

I have a library crate with few structs that contain references to each other:

pub struct A ();

pub struct B<'l> (pub &'l A);

pub struct C<'l> (pub &'l B<'l>);

I can use them fine if I just make one instance of each struct in fn main():

use mylib::{A, B, C}; // it is not my actually, I can't just avoid using these references in structs

fn main() {
    let a = A {};
    let b = B(&a);
    let c = C(&b);
}

But I want to write a function that returns instances of C, given an instance of A:

fn init_c<'l>(a: A) -> C<'l> {
    // create an instance here...
}

And I have some problems with lifetimes while trying to implement this function:

fn init_c<'l>(a: A) -> C<'l> {
    let b = B (&a);
    C::<'l> (&b)
}

This is failing with errors:

error[E0515]: cannot return value referencing local variable `b`
  --> src\main.rs:55:5
   |
55 |     C::<'l> (&b)
   |     ^^^^^^^^^--^
   |     |        |
   |     |        `b` is borrowed here
   |     returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing function parameter `a`
  --> src\main.rs:55:5
   |
54 |     let b = B (&a);
   |                -- `a` is borrowed here
55 |     C::<'l> (&b)
   |     ^^^^^^^^^^^^ returns a value referencing data owned by the current function

So how could I implement init_c?

sovesti
  • 35
  • 4

1 Answers1

2

There are two problems here:

  • The lifetime of b is tied to the function scope. No reference to b can be alive after the function returns. Since C::<'l>(&b) refers to b, you can't return it.
  • b itself refers to a. Your argument declaration a: A makes the function take ownership of a. So the lifetime of a also ends with the function scope.

If you want to have a C object, you need a living B object it can refer to. And your B object needs a living A object, too.

So one possible solution is to:

  1. Pass a by reference, and
  2. add an init_b function to make a B object.
fn init_b(a: &A) -> B {
    B(&a)
}

fn init_c<'l>(b: &'l B) -> C<'l> {
    C(&b)
}

fn main() {
    let a = A();
    let b = init_b(&a);
    let c = init_c(&b);
}

Playground

aedm
  • 5,596
  • 2
  • 32
  • 36