0

I'm using a library which has the concept of a Context which has to be initialized before using any other functions. I use that context to create objects to operate with. The context needs to be maintained as long as you're using those objects, so their type ties their lifetime to the Context. It looks somewhat like this:

struct Thing<'a, 'b> {
    aa: &'a i32,
    bb: &'b i32,
}

struct Context;

impl Context {
    fn init() -> Self {
        Context
    }
    fn thing<'a>(&'a self) -> Thing<'a, 'static> {
        // mock function, but it might require reading files or other
        // costly operations in order to create the `Thing`
        Thing { aa: &9, bb: &10 }
    }
}

I want to hold both the Context and one or more Things. I haven't been able to because creating both in the same function makes it so that I have to borrow Context, then move it to the struct, which is not allowed.

Is there a way to do this? Maybe a different way to create the Context so that it's clear it belongs to the struct rather than the function?

struct Friend<'a> {
    context: Context,
    thing: Thing<'a, 'static>,
}

impl Friend<'_> {
    fn new() -> Self {
        let context = Context::init();
        let thing = context.thing();
        Self { context, thing }
    }
}

fn main() {
    let boy = Friend::new();
    println!("value is {}", boy.thing.bb);
}

playground

error[E0515]: cannot return value referencing local variable `context`
  --> src/main.rs:28:9
   |
27 |         let thing = context.thing();
   |                     ------- `context` is borrowed here
28 |         Self { context, thing }
   |         ^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `context` because it is borrowed
  --> src/main.rs:28:16
   |
25 |     fn new() -> Self {
   |                 ---- return type is Friend<'1>
26 |         let context = Context::init();
27 |         let thing = context.thing();
   |                     ------- borrow of `context` occurs here
28 |         Self { context, thing }
   |         -------^^^^^^^---------
   |         |      |
   |         |      move out of `context` occurs here
   |         returning this value requires that `context` is borrowed for `'1`

In practice, Context is always going to be part of Friend, except for those few lines in the constructor function. The move exists because I'm not allowed to initialize the Friend before I have a Thing.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Pablo Tato Ramos
  • 409
  • 4
  • 18
  • @kmdreko Kind of. It explains how lifetimes relate to this problem, but I'd appreciate an answer with an alternative pattern or something I can use rather than just an explanation of why it's impossible. – Pablo Tato Ramos Mar 09 '20 at 11:52
  • @PabloTatoRamos, _I want to hold both the `Context` and one or more `Things`_ maybe you should detail this to get more help, why do you "want"? Otherwise i would just suggest to use a delegate function: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4e3dc2ba897dfcabd97c0ae36fd1f73e (it brings roughly same usage for an api) – Ömer Erden Mar 09 '20 at 12:45
  • @ÖmerErden `.thing()` isn't a free operation in my case, it requires reading files and so on. I will update the question to reflect this – Pablo Tato Ramos Mar 09 '20 at 12:50
  • *I'd appreciate an answer with an alternative pattern or something* — That would be covered in the linked duplicate under the section **How do I fix it?**. – Shepmaster Mar 09 '20 at 13:46
  • @Shepmaster I don't understand the part where you say "Place types that own data into a structure together". It sounds like that is what I'm doing, and that it contradicts the "not attempt to put these items in the same structure together" part in the previous sentence. It seems that I would need to move the `Thing` struct somewhere other than `Friend`? How would that look? If it's in a different structure, how could I have a method in `Friend` that accesses it? – Pablo Tato Ramos Mar 09 '20 at 13:51
  • 1
    Your `Context` owns data; your `Thing` does not, it only has references. The rest of that sentence says: "and then provide methods that allow you to get references or objects containing references as needed". You'll need to store `Context` separately from where you store your `Thing`s, but you can store the context *reference* in the `Friend`. [example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a2cbadecd7f42800f61e86cefe271645) – Shepmaster Mar 09 '20 at 14:23
  • @Shepmaster Thank you, that's exactly what I needed – Pablo Tato Ramos Mar 09 '20 at 14:29

0 Answers0