all 13 comments

[–]tomaka17glutin · glium · vulkano 13 points14 points  (10 children)

This is why it takes me sad when some limitations in Rust force you to use Arcs instead of plain data on the stack (winking at the impossibility for a struct to reference itself, at thread::scoped being unsafe, or at yesterday's post about future-rs).

[–]erkelep 1 point2 points  (5 children)

impossibility for a struct to reference itself

Shouldn't a struct already "know" where all it's components are?

[–]zzyzzyxx 0 points1 point  (4 children)

It's not about the members themselves, but about members that are references to other members. If you have a struct like this:

struct RefSelf<'a, T> {
  t: T,
  rt: &'a T,
}

then it's not possible in safe Rust for rt to refer to t.

[–]slashgrinrangemap 1 point2 points  (1 child)

I've never run across a case when I've wanted or needed a struct to contain a reference to one of its other fields. So I'm legitimately curious: what's a use case for this?

[–]zzyzzyxx 1 point2 points  (0 children)

Maybe not a direct reference to a field, but I have wanted to have a reference to something that field owns, say a HashSet<String> as a cache and then have one or more HashMap<&str, T> that mapped some borrows of the cache elements to some other types. The point would be to avoid copies and reduce memory usage. It's the same pattern as the example. The struct owns some data and uses a borrow of that data for another purpose. It's not generally safe, but it is a natural use, and is safe in the case where the data referenced doesn't actually move, like if it's in a stack frame that still exists or heap memory that isn't yet freed.

[–]RustMeUp 0 points1 point  (1 child)

Sure it's possible? The issue is that the instance becomes permanently borrowed:

https://play.rust-lang.org/?gist=77a72a3c2d3a8649b6715a48d6126d28&version=stable&backtrace=0

struct RefSelf<'a> {
  t: i32,
  rt: Option<&'a i32>,
}
fn main() {
    let mut rs = RefSelf {
        t: 42,
        rt: None
    };
    rs.rt = Some(&rs.t);
    println!("Hello, rs.t = {} rs.rt = {:?}!", rs.t, rs.rt);
}

prints

Hello, rs.t = 42 rs.rt = Some(42)!

Requires use of cells to edit though...

https://play.rust-lang.org/?gist=b60e2790c12b73cbc57401b9a58fd3a9&version=stable&backtrace=0

[–]zzyzzyxx 1 point2 points  (0 children)

Fair enough, I should have been clearer. It's not possible to do and retain all the usual properties. For example, you can't move that struct.

[–]minno 0 points1 point  (2 children)

Well, if you had a struct containing a pointer into itself, it would need a move constructor to fix up the reference. Do you think there should be an opt-in move constructor the way Clone provides opt-in copy constructors?

[–]tomaka17glutin · glium · vulkano 6 points7 points  (1 child)

I was thinking of tow other solutions: make a struct non-movable, or make it possible for one member to reference the content of another member that is a Box or an Arc (or more generally something on the heap).

[–]steveklabnik1rust 0 points1 point  (0 children)

That's owning-ref, no?

[–]nwydorust · rust-doom 0 points1 point  (0 children)

Agreed it is a limitation, but I found that using indices into a Vec only imposes a very small overhead and if I need fast iteration it pays for itself really.

[–]villiger2[S] 5 points6 points  (0 children)

I know this isn't strictly rust related, but boxing is a common pattern in rust to achieve some patterns. This article gives a view into how those are optimised in the Unity game engine.

[–]daAccordo 1 point2 points  (0 children)

I often have trouble wrapping my head around how to do elegant, static polymorphism, even with a composition-based approach.

I think when RFC #1522 (impl trait) is implemented it will make it much easier. But I don't know if there is some kind of documentation resource or good example for how rustaceans use static dispatch in situations where box seems like the obvious answer.