3 ms·
> Please don’t. This is probably one of the biggest mistakes I see in people coming from languages with garbage collection to languages like Rust or C++. They a
by pbiggar 6y ago
> Please don’t. This is probably one of the biggest mistakes I see in people coming from languages with garbage collection to languages like Rust or C++. They attempt to use smart pointers as a replacement for garbage collection.
Thanks for the feedback. I'm new here, so the advice is appreciated.
I've been using the rule of thumb "does the lifetime of this variable exceed the lifetime of this function" to decide whether to put it in an Rc. Given I'm implementing an interpreter for a GCed language, it seems like I'm pretty much going to have to put everything into an Rc, no? (Another commented mentioned an Arena, but either way it's not the stack).
- Jweb_Guru 6y agoFWIW, very often the answer in those "returning from a function" situations (in Rust anyway) is to allocate further up the stack, or clone, rather than use a smart pointer. In fact, you can nearly always do this, as long as you control enough of the involved code. Use of Rc is intended not only for data that outlives the function in which it's currently defined (because in general, you can define stuff wherever you want), but for data whose lifetime is unpredictable--there either isn't a safe point in your program where you can definitively drop it (this usually more applies to Arc, if it's holding data pointed at by two independent threads), or there is a known safe point but it is too imprecise to be useful to you. But the latter is pretty subjective; for instance, if you know it's safe to drop something when the program ends, but it might be safe to drop much earlier, in some contexts it still might be worth dropping at the end rather than dealing with the significant overhead of something like Rc or Arc. The reason I recommended arenas is that I find in compilers, often you have cases where you just need to define a scope at some point in your program way higher than when you create the value, and that's what they excel at--letting you initialize stuff with whatever parent scope you want, not arbitrarily restricting you to the function boundary.
- zozbot234 6y agoYou should only put an object in an Rc when its lifetime may potentially be determined by multiple, independent references. That's what it's for. For the simpler case of single ownership, Box<> is fine,
- saghm 6y agoYou can return stack values assuming you're willing to transfer ownership to the calling function. For example, this is totally valid (and does not heap allocate): struct Point { x: i32, y: i32, } fn add(p1: &Point, p2: &Point) -> Point { Point { x: p1.x + p2.x, y: p1.y + p2.y, } } You'll also find that a lot of common data structures already use the heap under the hood (e.g. Vec and HashMap), so there's no need to Box them. I'm not quite sure what you mean by "the lifetime of a function"; lifetimes are associated with references, but not with functions themselves. In particular, it can be totally valid to return a reference to something that needs to last beyond the current function call; lifetimes are used to enforce that you'll get a compiler error if the memory your reference points to won't survive long enough. For example, it's totally valid to have a function return a reference to a field on a struct as long as the struct was also passed in by reference: fn x_val(p: &Point) -> &i32 { &p.x } Since `p` is passed in by reference, the compiler knows that the reference to `x` will still be valid after the function returns, so it allows that reference to be returned. The compiler will even catch it if later you drop the Point while the reference to the `x` value is still alive: fn main() { let p = Point { x: 1, y: -1 }; let x: &i32 = x_val(&p); std::mem::drop(p); // Without this line, this will compile, since the compiler can tell that `x` isn't // used after `p` is dropped. However, since `x` is used here afterwards, the // compiler will generate an error stating that `p` does not live long enough. println!("{}", x); }