I've tried things like this:
const b: Box<i32> = Box::new(5);
Which gave me that function calls in constants are limited to struct and enum constructors.
I also tried
const b: Box<i32> = box 5;
Which gave me an error saying that I should use Box::new instead. Is there any way to do this? I need a box because it's in a struct and the struct requires a box.
Edit:
I know that Box::new is a function, and I can't use that in a const item. But is there another way to create a box that is allowed?
Not right now, not in the immediate future.
As #Paolo mentioned, the only way to initialize a const variable is to use a constant expression. Today, in stable, it is limited to a restricted set of operations (some integers manipulation, some casts, ...).
There is a RFC to extend the set of expressions available in constant expressions: const fn. It is about allowing functions (both free functions and methods) to be marked const, making them available in constant expressions.
The tracking issue is #24111, and const fn can be used on nightly with the #![feature(const_fn)] crate attribute...
... however, at the moment, const fn are mostly about integral manipulations too. There is no plan that I know of to extend to arbitrary (side-effect-less) expressions, and thus it would not work for Box::new.
At the moment, you are advised to use lazy_static!, it will not allow the item to be const (it will be initialized on first use).
As the const and static chapter of the book says:
Both const and static have requirements for giving them a value. They
may only be given a value that’s a constant expression. In other
words, you cannot use the result of a function call or anything
similarly complex or at runtime.
Box::new() is a function, so it falls in the scope of things you can't do to initialize a const.
You can look at lazy-static for a way to initialize a static variable at runtime.
Related
I have been dabbling in experimental features lately and have been using them for a library I'm building. I am trying to reduce the size of an enum by using ThinBox<[T]> to store contents in a fixed length array without the whole const generics monomorphization business happening in my code (since I need to store this in an enum later and don't want to have a const generic on the level of the enum).
The closest thing I got to a solution is to ThinBox a fixed sized array. (it coerces to a slice). Though it technically does fix the problem of const generics on the type level, I want to find a solution that doesn't require me to input const generics into a function (since it's a lot less flexibility). I also don't want to end up with a ThinBox<&[T]> since that is two levels of indirection.
Is there a method, safe or unsafe, that can initialize a ThinBox<[T]> without directly hacking the compiler?
You can use ThinBox::new_unsize like this:
ThinBox::<[T]>::new_unsize([/* your array */])
I'm learning Rust, and so far, there appears to be 3 ways to declare variables:
const A: u8 = 42;
static A: u8 = 42;
let A: u8 = 42;
I get that you can't have a mutable const, and the compiler will warn you if it's not all uppercase, and that when you use a const, the equivalent of the C pre-compiler will replace every place that A appears with a literal 42 (it won't have a consistent memory address).
From a practical standpoint, I don't see a difference between any of these in their immutable form. None of them can be mutated. They can all be used exactly the same. What's the difference?
- const defines values, which are replaced in the according code
- static defines global variables representing memory addresses, ie read-only memory, global atomic counters or locks (+initialization), C-abi interaction, etc
- let defines scoped variable bindings
You can not use let as global variable (static lifetime), so static fits that role. Note, that it also exists as lifetime annotation.
Reference explanation
What's the difference?
The question can be easily reduced to const vs. immutable static because that is the only case that is pretty similar (let is intended to introduce variables to the current scope).
const is supposed to be used when you don't mind the value to be inlined all over the place. Typically, that means small objects; commonly constant integers. Immutable static is used otherwise.
In practice, for most use cases (specially for private items), when considering optimizations done by LLVM, there is usually little difference.
You might need to take a closer look if codegen was particularly bad, if you want to export an object and related low-level concerns.
I would like to initialise some constants at the very top of my main.rs file, like so:
const PRIVATE_KEY: Vec<u8> = std::fs::read("./jwtRS256.key").unwrap();
const PUBLIC_KEY: Vec<u8> = std::fs::read("./jwtRS256.pub.key").unwrap();
fn main () {
// do some stuff
}
However, I am getting a compiler error as follows:
error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> src/main.rs:16:30
|
16 | const PRIVATE_KEY: Vec<u8> = std::fs::read("./jwtRS256.key").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I understand; makes sense. The compiler wants to be able to evaluate the values of these constants at compile time and it can't. What is the alternative, though? I need those constants available all through my code, including in other modules. So, I cannot just move them as regular variables into the main function. What do I do instead? (I am on Rust 1.47.)
THere are two possibilities and it's a bit unclear which one you want:
Embed the content of the files in the binary
include_bytes and include_str will read the files during compilation and embed their contents (according to the machine having compiled the binary) in the final program.
Runtime global
If you want to read the files when the program starts and make those contents available globally, you want lazy_static or once_cell instead, they allow running code to initialise a global at runtime.
const v static
static is probably what you want either way, it's much closer to globals in other languages.
const is more macro-ish, all references to the const's name will be replaced by the value, directly.
This is often what you want for e.g. simple numbers (that way they get compiled as immediates rather than loads from memory), but for more complex types or large amounts of data static is generally the better pick.
If these files are present and compile-time and your only goal is to include them in the compilation, then you can simply use include_bytes!("./jwtRS256.key"). However, if you want to read these files at runtime, then consider using lazy_static:
lazy_static! {
pub static ref PRIVATE_KEY: Vec<u8> = std::fs::read("./jwtRS256.key").unwrap();
}
This crate essentially allows you to lazily initialize static variables and use them anywhere.
In C++, you have the ability to pass integrals inside templates
std::array<int, 3> arr; //fixed size array of 3
I know that Rust has built in support for this, but what if I wanted to create something like linear algebra vector library?
struct Vec<T, size: usize> {
data: [T; size],
}
type Vec3f = Vec<f32, 3>;
type Vec4f = Vec<f32, 4>;
This is currently what I do in D. I have heard that Rust now has Associated Constants.
I haven't used Rust in a long time but this doesn't seem to address this problem at all or have I missed something?
As far as I can see, associated constants are only available in traits and that would mean I would still have to create N vector types by hand.
No, associated constants don't help and aren't intended to. Associated anything are outputs while use cases such as the one in the question want inputs. One could in principle construct something out of type parameters and a trait with associated constants (at least, as soon as you can use associated constants of type parameters — sadly that doesn't work yet). But that has terrible ergonomics, not much better than existing hacks like typenum.
Integer type parameters are highly desired since, as you noticed, they enable numerous things that aren't really feasible in current Rust. People talk about this and plan for it but it's not there yet.
Integer type parameters are not supported as of now, however there's an RFC for that IIRC, and a long-standing discussion.
You could use typenum crate in the meanwhile.
There are comparatively many storage class specifiers for functions arguments in D, which are:
none
in (which is equivalent to const scope)
out
ref
scope
lazy
const
immutable
shared
inout
What's the rational behind them? Their names already put forth the obvious use. However, there are some open questions:
Should I use ref combined with in for struct type function arguments by default?
Does out imply ref implicitely?
When should I use none?
Does ref on classes and/or interfaces make sense? (Class types are references by default.)
How about ref on array slices?
Should I use const for built-in arithmetic types, whenever possible?
More generally put: When and why should I use which storage class specifier for function argument types in case of built-in types, arrays, structs, classes and interfaces?
(In order to isolate the scope of the question a little bit, please don't discuss shared, since it has its own isolated meaning.)
I wouldn't use either by default. ref parameters only take lvalues, and it implies that you're going to be altering the argument that's being passed in. If you want to avoid copying, then use const ref or auto ref. But const ref still requires an lvalue, so unless you want to duplicate your functions, it's frequently more annoying than it's worth. And while auto ref will avoid copying lvalues (it basically makes it so that there's a version of the function which takes an lvalues by ref and one which takes rvalues without ref), it only works with templates, limiting its usefulness. And using const can have far-reaching consequences due to the fact that D's const is transitive and the fact that it's undefined behavior to cast away const from a variable and modify it. So, while it's often useful, using it by default is likely to get you into trouble.
Using in gives you scope in addition to const, which I'd generally advise against. scope on function parameters is supposed to make it so that no reference to that data can escape the function, but the checks for it aren't properly implemented yet, so you can actually use it in a lot more situations than are supposed to be legal. There are some cases where scope is invaluable (e.g. with delegates, since it makes it so that the compiler doesn't have to allocate a closure for it), but for other types, it can be annoying (e.g. if you pass an array be scope, then you couldn't return a slice to that array from the function). And any structs with any arrays or reference types would be affected. And while you won't get many complaints about incorrectly using scope right now, if you've been using it all over the place, you're bound to get a lot of errors once it's fixed. Also, its utterly pointless for value types, since they have no references to escape. So, using const and in on a value type (including structs which are value types) are effectively identical.
out is the same as ref except that it resets the parameter to its init value so that you always get the same value passed in regardless of what the previous state of the variable being passed in was.
Almost always as far as function arguments go. You use const or scope or whatnot when you have a specific need it, but I wouldn't advise using any of them by default.
Of course it does. ref is separate from the concept of class references. It's a reference to the variable being passed in. If I do
void func(ref MyClass obj)
{
obj = new MyClass(7);
}
auto var = new MyClass(5);
func(var);
then var will refer the newly constructed new MyClass(7) after the call to func rather than the new MyClass(5). You're passing the reference by ref. It's just like how taking the address of a reference (like var) gives you a pointer to a reference and not a pointer to a class object.
MyClass* p = &var; //points to var, _not_ to the object that var refers to.
Same deal as with classes. ref makes the parameter refer to the variable passed in. e.g.
void func(ref int[] arr)
{
arr ~= 5;
}
auto var = [1, 2, 3];
func(var);
assert(var == [1, 2, 3, 5]);
If func didn't take its argument by ref, then var would have been sliced, and appending to arr would not have affected var. But since the parameter was ref, anything done to arr is done to var.
That's totally up to you. Making it const makes it so that you can't mutate it, which means that you're protected from accidentally mutating it if you don't intend to ever mutate it. It might also enable some optimizations, but if you never write to the variable, and it's a built-in arithmetic type, then the compiler knows that it's never altered and the optimizer should be able to do those optimizations anyway (though whether it does or not depends on the compiler's implementation).
immutable and const are effectively identical for the built-in arithmetic types in almost all cases, so personally, I'd just use immutable if I want to guarantee that such a variable doesn't change. In general, using immutable instead of const if you can gives you better optimizations and better guarantees, since it allows the variable to be implicitly shared across threads (if applicable) and it always guarantees that the variable can't be mutated (whereas for reference types, const just means only that that reference can't mutate the object, not that it can't be mutated).
Certainly, if you mark your variables const and immutable as much as possible, then it does help the compiler with optimizations at least some of the time, and it makes it easier to catch bugs where you mutated something when you didn't mean to. It also can make your code easier to understand, since you know that the variable is not going to be mutated. So, using them liberally can be valuable. But again, using const or immutable can be overly restrictive depending on the type (though that isn't a problem with the built-in integral types), so just automatically marking everything as const or immutable can cause problems.