Returning (Struct1, Struct2) in a function where one struct references another

I have a struct called Lexer, and I have another struct called Parser. Parser 's new(l: &mut Lexer) method takes a mutable instance of Lexer as input.

In my tests I set them up like this:

        let input = "
let x = 5;
let y = 10;
let foobar = 838383;";
        let mut l = Lexer::new(input);
        let mut p = Parser::new(&mut l);

I want to extract a setup() function that does this step for me cuz it's repeated in all my tests:

    fn setup(input: &str) -> (Lexer, Parser) {
        let mut l = Lexer::new(input);
        let mut p = Parser::new(&mut l);
        return (l, p);
    }

but then I get this error:

   --> parser/src/parser.rs:112:16
    |
111 |         let mut p = Parser::new(&mut l);
    |                                 ------ `l` is borrowed here
112 |         return (l, p);
    |                ^^^^^^ returns a value referencing data owned by the current function

I thought since I'm returning both l and p from the function then maybe the ownership rules would let me, because after the function execution both l and p will be owned by the caller's scope, so technically this should still be fine as far as ownership rules go?

I may be wrong, but any help is greatly appreciated :slight_smile:

No, it's not fine – it would cause a dangling pointer/reference. The reference inside Parser would point to the old place of the lexer, which would have been invalidated by the time the function returned (due to being moved to its new place, inside the caller's stack frame).

You should just make the parser own the lexer instead.

1 Like

Alternatively you could use a macro... though I personally don't think you're saving much.

macro_rules! declare_lexer_parser {
    ($lexer:ident, $parser:ident, $input:expr) => {
        let mut $lexer = Lexer::new($input);
        let $parser = Parser::new(&mut $lexer);
    }
}
// ...
declare_lexer_parser!(l, p, &s);
p.parse(...);

The title of this thread describes what is typically called a self-referential struct. Rust's ownership and borrowing model makes self-referential structs almost impossible to use at all without unsafe, or to use soundly using unsafe. If you're curious about the topic, you can search for the term to find many prior discussions.

1 Like

ah makes sense, thanks

got it, yeah I'll either refactor or use the macro. thanks for the explanation, I'll look up self-refrential structs :slight_smile: