How to create a borrow to itself

Disclaimer: this question is 100% useless. I'm just trying to figure out if this is possible for fun.

I'm wondering if it's possible to find a value of type A, where A is defined as

struct A(&'static A);

in a "legit" way, that is, no diverging, and no UB. I have tried many things, but I'm always stuck at the point where I need to transmute (or equivalent) something that would be a valid A to type A, but I can't do that in-place, and thus it would invalidate the borrow.

Wel, since a reference always needs to point to a valid instance it seems like that would either be infinitely recursive or cyclic and I think that neither is possible in rust.

Yes. For a "fun" time, run this:

#[derive(Debug)]
struct A(&'static A);

static B: A = A(&B);

fn main() {
    println!("{:?}", &B);
}
7 Likes

To state explicitly what the above code demonstrates: a cyclic structure is possible when declared as static. It is hazardous because std::fmt::Formatter has no cycle detection (and in general Rust's standard derives do not, so == and so on will also stack overflow), but if you don't do any of those things (most easily accomplished by not using any standard derives) then it will be fine.

1 Like

Another approach which avoids static is to use Cell<Option<...>> to delay setting the reference. The resulting value isn't movable.

use std::cell::Cell;

#[derive(Debug)]
struct A<'a>(Cell<Option<&'a A<'a>>>);

fn main() {
    let x = A(Cell::new(None));
    x.0.replace(Some(&x));
    println!("{:?}", &x);
}
4 Likes

Well, I'm astonished. It's much simpler than anything I've tried (which is probably why it works, and not what I tried :stuck_out_tongue: ). However, that trick doesn't work if I want an exclusive borrow:

struct A(&'static mut A);

static mut B: A = unsafe { A(&mut B) }; // Error

See the playground.

When I asked the question, I wasn't sure if it was possible to build such a value in pure Rust, but in theory there is nothing that would prevent its existence (at least not that I know of). A pointer to itself, seen as A would be perfectly valid.

Yeah but this way you really bypass the problem, don't you?

Making something like

struct A<'a> {
    this: &'a mut A<'a>,
    any_other_field: u32,
}   

point to itself would be UB [1] as you given a &mut self, you could split the borrow with &mut self.this.any_other_field and self.any_other_field, thus obtaining two aliasing &mut to the same data.


  1. or at least wildly unsound ↩︎

4 Likes

Of course it works! And you don't even need to write a lot:

struct A(&'static mut A);

Done.

static mut B: A = unsafe { A(&mut B) }; // Error

That's not exclusive borrow. That's two non-exclusive mutable borrows. Variable binding is also a borrow and thus couldn't exist simultaneously with inner &'static reference, it would violate exclusivity!

Because any attempt to touch such object would be violation of “no more than one active exclusive reference at the time” rule you may imagine that world is filled with such objects, you just can't touch them and thus by using as if rule compiler optimizes them all out.

1 Like

I'm not sure to understand, my purpose is to get a value whose type is A, not just to define type A.

Edit: ok I understood what you mean.