What prevents Arc from having a const constructor?

Having a const Mutex or RwLock wrapped around a T (with a constant constructor) is very useful for removing the need of using lazy_static (clean looking code). While using this in my code, I wondered, "Why can't there be a const constructor for Arc?"

Because an Arc allocates memory.

8 Likes

Aside from the technical obstacles, what would you use it for?

Arc keeps a thing alive until the last reference is dropped. But statics can never be dropped, so even if Arc::new was const, it seems pointless to use it to initialize a static Arc<T> -- you might as well just use a &'static T and save yourself the trouble of updating the reference count every time you clone it or drop a copy. Right?

5 Likes

When you put it like that, that makes sense. Yeah, it's pretty much pointless to use Arc in a static. I was more just wondering about the underlying why to the question.

By "allocates memory" you implicitly mean heap-allocation? When I declare a const constructor in a static, is that not heap-allocated? This would imply stack-allocation, correct? If so, this would extend my mental model of stack frames. I would think that static items imply heap-allocation, since we need a fixed memory address

This "fixed memory address" in case of statics will refer to some part of the program itself, not to the memory allocated for it.

1 Like

Yeah, right as you said this, I realized that String had a const constructor, invalidating my theory

Yes I mean a heap-allocation. A String has a const constructor because an empty string doesn't allocate memory.

2 Likes

It's not. It's allocated at compile time, inside a special segment of the resulting executable/program image, which will be mapped to operative memory by the dynamic loader/linker once the program is started.

4 Likes

It's pointless as a top-level static, but I can imagine some edge cases where it could be useful inside some other type. A static Mutex<Arc<_>>, for example, could contain an initial compile-time-specified value that gets switched out for a different Arc at runtime.

Those cases rare enough that I expect it's not worth adding the complexity inside Arc to make it work, though.

2 Likes

No.

You can think of the compiler as copy-pasting the definition of a const everywhere it is used.

A static variable will be stored in the data section of a binary, which the kernel makes accessible from your program's address space when it is loaded into memory.

5 Likes

Thanks for the great clarifications. I never knew how statics/consts worked under the hood (I'm sure there's more to it too)!

1 Like

Looking deeper it seems like The Reference is deliberately vague on the subject. I'm assuming this is because the specifics are up to the implementation, in this case LLVM and the linker.

Constant items:

A constant item is an optionally named constant value which is not associated with a specific memory location in the program. Constants are essentially inlined wherever they are used, meaning that they are copied directly into the relevant context when used. References to the same constant are not necessarily guaranteed to refer to the same memory address.

Static Items:

A static item is similar to a constant, except that it represents a precise memory location in the program. All references to the static refer to the same memory location.

2 Likes

If it's just for clean looking code, have a look at Lazy from once_cell, once_cell::sync::Lazy - Rust . Maybe it suits your tastes better. Or not, of course.

I would say that technically, allocation is not an issue for const-ness.

From a language + compiler point of view, if the value returned from a const function has heap-allocated memory, it can easily be translated into reserving memory in the binary for it and baking the values there.

I think a greater difficulty is deallocation, or modification. For example, if you were to return a Cell<Vec<u8>> currently containing "Hello" and store that in static, it would be problematic. If someone attempts to replace "Hello" by "Lorem ipsum..." and triggers growth, the memory reserved for "Hello" cannot be freed by the Global Allocator.

So in short: I think that allocation + no-deallocation may eventually be manageable, however allocation + potential run-time deallocation may remain unreachable.

1 Like

I remember that @oli_obk talked about const allocations at RustConf 2019:

4 Likes

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.