Question about returning borrowed content [solved]


#1

I’ve been fighting the compiler for a while, and I’m not sure what I’m doing wrong. Here is a simplified example:

 extern crate byte_stream_splitter;

  use byte_stream_splitter::ByteStreamSplitter;
  use std::io::{StdinLock};

  pub fn msg_stream<'a>(stdin: &'a mut StdinLock<'a>, separator: &'a [u8]) ->  ByteStreamSplitter<'a, &'a mut StdinLock<'a>> {

            let splitter = ByteStreamSplitter::new(&mut stdin, &separator);

            splitter
        }


  fn main() {
      println!("Hello, world!");
  }

And here’s the error:

error[E0597]: `stdin` does not live long enough
  --> src/main.rs:9:55
   |
9  |           let splitter = ByteStreamSplitter::new(&mut stdin, &separator);
   |                                                       ^^^^^ borrowed value does not live long enough
...
12 |       }
   |       - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 6:1...
  --> src/main.rs:6:1
   |
6  | / pub fn msg_stream<'a>(stdin: &'a mut StdinLock<'a>, separator: &'a [u8]) ->
7  | | ByteStreamSplitter<'a, &'a mut StdinLock<'a>> {
8  | |
9  | |           let splitter = ByteStreamSplitter::new(&mut stdin, &separator);
10 | |
11 | |           splitter
12 | |       }
   | |_______^

So, it seems to me this error is saying that the StdinLock reference that is being passed into the function is getting dropped at the end of the msg_stream function, but the ByteStreamSplitter wants to hold onto that same reference. Is that correct?

So what’s the idiomatic way of returning an object that contains a borrowed value?


#2

You’re passing a &mut &mut StdinLock and &&[u8] to ByteStreamSplitter::new. What you actually want is to pass those arguments in directly I think.


#3

Ahh, you’re absolutely right! I just sort of glossed over how I was taking a reference to a reference when I was instantiating splitter.

I just had to pass the 2 arguments directly, and tweak the return type a bit. This compiles:

pub fn msg_stream<'a, 'b>(stdin: &'b mut StdinLock<'a>, separator: &'a [u8]) ->  ByteStreamSplitter<'a, StdinLock<'b>> {

            let splitter = ByteStreamSplitter::new(stdin, separator);

            splitter
        }

#4

I think you’ll want a signature like this instead:

pub fn msg_stream<'a, 'b>(stdin: &'b mut StdinLock<'a>, separator: &'b [u8]) -> ByteStreamSplitter<'b, StdinLock<'a>>

Otherwise you run the risk of StdinLock borrow (in the caller) not living long enough.


#5

Thanks for the heads up. If I understand what you wrote correctly, I’m surprised that my function signature was valid/sensical. Why did my code compile with my original signature? Was it because I didn’t actually call the function?


#6

It’s a valid signature, it just may prevent callers from actually satisfying the lifetime requirements. It’s somewhat similar to how the following compiles but ends up borrowing the struct for its entire lifetime, which is probably non-sensical in practice:

struct Foo<'a>(&'a [u8]);

impl<'a> Foo<'a> {
    fn borrow_forever(&'a mut self) {}
}

If you’ve not tried calling your fn, it’s a good idea to do so to make sure it behaves (in terms of borrows) as expected. It’s completely possible to define functions (or types) in Rust that compile on their own but then aren’t really usable by callers.