0

Say a B contains a reference to an A:

struct A;

struct B<'t> {
    a_ref: &'t A,
}

My goal is to write a simple function f that makes the following two equivalent (though one may use the heap).

let (a, b) = f();
let a = A { };
let b = B { a_ref: &a };

How would I write f? Here's an attempt:

fn f<'t>() -> (A, B<'t>) {
    let a = A { };
    let b = B { a_ref: &a };
    (a, b)
}

But it fails to compile with two errors, which I understand but am not sure how to avoid:

error[E0515]: cannot return value referencing local variable `a`

error[E0505]: cannot move out of `a` because it is borrowed

I have seen Why can't I store a value and a reference to that value in the same struct?. Above, I wrote two lines of code that create an A and a B. My question is not why my attempt to abstract those two lines fails. My question is how to abstract (something like) those two lines.

Yes, my attempt tries to store a value and a reference to that value in a struct, but whatever, that is not the goal. I know that does not and cannot work. That is not what I'm trying to accomplish. My goal is something unrelated and which may be possible regardless. For example, returning a closure or using a macro could work.

dolay
  • 56
  • 4
  • 1
    Your edits have not made the question not a duplicate. "My goal is to write a simple function `f` [that returns something self-referential]" is sufficiently answered by the other question (not possible with `&` references, but could be done with raw pointers, `Arc`, etc.) If you edit your question to more specifically explain what you want, I think this question could be reopened. For instance, why does `let a = make_a(); let b = make_b(&a)` not work for you? That is the normal way of creating things that reference other things in Rust. – trent Jun 02 '20 at 16:55
  • Because `a` and `b` are created together by the same algorithm. Thanks for pointing that out. I will think about how to clarify that in the question. – dolay Jun 02 '20 at 17:01
  • 1
    [An example of what @trentcl says about using an `Rc`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=01888077887e988043e313ff03b79894). – Shepmaster Jun 02 '20 at 17:24

1 Answers1

3

So the problem here is that f is giving out ownership of a to its caller:

  1. a is created inside f. a belongs to f.
  2. f returns a, transferring ownership to its caller.
  3. a is now moved from f to the caller.

The problem with moves is that they have the right to move the variable in memory. This would invalidate all references to that variable, and therefore no references are allowed to a variable whenever it is moved.

What you could do is have f receive a reference to a and return a new B with a reference to a: example.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
felix91gr
  • 103
  • 6
  • I already understand why `f` does not work. My question hasn't been answered, but thanks for suggesting a partial alternative. – dolay May 31 '20 at 00:36
  • 1
    <3. For what it's worth, I also thought of using a macro (of the `macro_rules! f { ... }` form) but I did not manage to make it work. It still resulted in a move of the tuple `(a, b)` which of course cannot be done. Maybe there's a way of making it happen with a macro, but my macro-fu is not there yet to tell one way or the other. – felix91gr May 31 '20 at 06:28
  • 1
    Macros could work, but I also had no idea where to begin with them. They are the kind of cleverness I imagined in an answer :) Another partial solution might be for `f` to return a pair `(a, b_maker)` where `b_maker` takes a `&A` and returns a `B`. Then maybe use macros to automate – dolay Jun 02 '20 at 16:33