How to move box while hold reference to inner value

The following code does not compile.

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

But if ignoring borrow checker, this code should work.
Because after a is moved to c,
the address of inner data does not change,
so the reference is still valid.

fn main() {
   let a = Box::new(1);
   let b:&i32 = &*a;
   let c = a;
   println!("{}", b);
}

What is the common practice for this problem?

For example, I have the following simple struct.
How to initialize this?

struct Foo<T> {
   a: Box<T>
   b: &T,
}

The borrow checker doesn't know about the heap or the stack (or any other memory region). So it's not possible to convince it (in safe code) to allow this kind of pattern.

Search this forum for the expression "self-referential type". The TL;DR is that it's not possible in safe code. You should keep your owned data and references to it in separate types.

Your struct is redundant anyway: if you have a Box<T>, you can always just obtain a reference to it, so you don't really need the reference field.

What is the current common practice for self-referential type?

It is "don't". You rearrange your data structure so that it isn't self-referential.

If you really-truly-positively need a self-referential type, you can try some of the 3rd-party crates, but I'm intentionally not going to recommend any of them, as even the most popular one contains soundness bugs.

1 Like

The fallancy in this reasoning is that when you move something you have complete ownership over it, which means you can also get mutable access to it, which is in contradiction with the fact that you already have an immutable reference.

This is even more clear in this example since there's nothing preventing you from doing foo.a = Box::new(t); and making the b reference dangling.

1 Like

I realize there is no unsafe or special crates need to initialize

struct Foo<T> {
   a: Box<T>
   b: &T,
}

where b is reference to the inner value of a

I can initialize Foo by making b some unused static reference.
Then modified it to pass model checker.

struct Foo<'a> {
    a: Box<i32>,
    b: &'a i32,
}

fn main() {
    static INT: i32 = 1;
    let mut foo = Foo {
        a: Box::new(2),
        b: &INT
    };
    
    foo.b = foo.a.as_ref();
}

In my real code, I have lots of types that need to initialize "partially" like above first,
to pass the borrow checker.

To get a unused but valid reference easily,
I implement Default trait for many &T types,
This also allows me to #[derive(Default)] for structs that contains reference.

Is this a good practice?

That code is valid as-is but you will find that you cannot do anything useful with the Foo afterward. For example, change the end of your program to be:

    ...
    foo.b = foo.a.as_ref();
    use_foo(&mut foo);
}

fn use_foo(foo: &mut Foo<'_>) {}

This will not compile:

error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable
  --> src/main.rs:16:13
   |
15 |     foo.b = foo.a.as_ref();
   |             -------------- immutable borrow occurs here
16 |     use_foo(&mut foo);
   |             ^^^^^^^^
   |             |
   |             mutable borrow occurs here
   |             immutable borrow later used here

In general, you can construct this kind of self-referential structure, but you will be unable to do anything with it that you couldn't have done with local variables a and b not contained in a struct.

1 Like

My current use-case is parsing a read-only file.
So the struct can remain read-only until dropped.
So is this a good practice for my use case?

In that case — you only ever use &Foos — it would work, yes. But I would still recommend avoiding the struct and using separate variables, unless you have some specific use for needing to pass the &Foo around a lot.

You can also consider defining a struct that is all borrowed — contains only references and not the Box. There is no problem with passing that around or even mutating it.

2 Likes

A common pattern for this use-case, is to have the struct borrow from the source of truth data, rather than own the source of truth data and have references back into it. So your code looks something like:

struct FileData {
    data: Vec<u8>,
}

struct BorrowedZone<'a> {
    zone_name: &'a str,
    point_of_contact: &'a ContactDetails,
    location: &'a Location
}

struct Zone {
    point_of_contact: ContactDetails,
    location: Location,
}

impl Zone {
    fn from_borrowed_zone(zone: BorrowedZone<'_>) -> (String, Zone) {
        (zone.zone_name.to_string(),
         Self {
             point_of_contact: zone.point_of_contact.to_owned(),
             location: zone.location.to_owned(),
        }
    }
}

fn decode_file<'a>(file_data: &'a FileData) -> BTreeMap<&'a str, BorrowedZone<'a>> {
    let mut map = BTreeMap::new();
    for zone in read_zones(file_data) {
        map.insert(zone.zone_name, zone);
    }
}

The compiler then knows that the return value of decode_file borrows from the supplied FileData, and will insist that the owned data is kept alive for as long as the data that borrows from it is alive. Once you've extracted the interesting data, you can turn it into owned data, and drop the borrowed form and the source file if you so desire.

There's also the rabbit hole of ways to handle Zone versus BorrowedZone - you might make BorrowedZone a Cow<'a, (String, Zone)>, for example, or put the zone name into Zone, or otherwise make changes.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.