Can't assign results of generic functions to `const` variables
fn foo<T:Default>() {
const bar : T = T::default();
}
gives the following error (twice for some reason):
$ cargo +nightly build
Compiling arrys_sizeoof v0.1.0 (C:\work\trash\repros\arrys_sizeoof)
error[E0401]: can't use type parameters from outer function
--> src\lib.rs:2:17
|
1 | fn foo<T:Default>() {
| --- - type variable from outer function
| |
| try adding a local type parameter in this method instead
2 | const bar : T = T::default();
| ^ use of type variable from outer function
error[E0401]: can't use type parameters from outer function
--> src\lib.rs:2:21
|
1 | fn foo<T:Default>() {
| --- - type variable from outer function
| |
| try adding a local type parameter in this method instead
2 | const bar : T = T::default();
| ^^^^^^^^^^ use of type variable from outer function
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0401`.
error: Could not compile `arrys_sizeoof`.
To learn more, run the command again with --verbose.
Changing let to const to let makes it compile just fine.
Another, slightly different example:
fn get_size<T:Sized>() {
const size : usize = std::mem::size_of::<T>();
}
Here, the type of the variable is non-generic now, and it's only a function that is. Results in the same error, but only once now:
$ cargo +nightly build
Compiling arrys_sizeoof v0.1.0 (C:\work\trash\repros\arrys_sizeoof)
error[E0401]: can't use type parameters from outer function
--> src\lib.rs:2:46
|
1 | fn get_size<T:Sized>() {
| -------- - type variable from outer function
| |
| try adding a local type parameter in this method instead
2 | const size : usize = std::mem::size_of::<T>();
| ^ use of type variable from outer function
error: aborting due to previous error
For more information about this error, try `rustc --explain E0401`.
error: Could not compile `arrys_sizeoof`.
To learn more, run the command again with --verbose.
P.s.: if this is the desired behavior, it is confusing, since 1) changing const to let should not result in type error, 2) rustc --explain E0401 doesn't mention anything close - all the examples are about definition of new functions/types inside the function body (and it's unclear how to adapt that knowledge to this particular use case).
P.p.s.: the behavior is exactly the same on current stable (1.32.0)
Well, what's intended here is that you're not allowed to use type parameters of an enclosing item in an inner item. const, static, trait, fn, impl are items, while let is a statement, so it's allowed in the latter case. I don't see an actual typing error though. And it makes sense that the error only shows once in the second example, because there you only use T once and not twice.
Yep; @jonas-schievink is correct. Rust does not support what is referred to in Haskell as ScopedTypeVariables. Moreover, even if you could use the type parameter T, calling T::default() would fail since it's not a const expression.
cc @estebank re. possible improvements to diagnostics.
cc https://github.com/rust-lang/rust/issues/57563.
We need to stop suggesting adding a type argument when the requirement comes from a const, as they can't be generic, and replace it with a note stating that.
From #48427, the same behavior is seen for statics
fn x<T: Default>() {
static a: T = T::default();
}
https://github.com/rust-lang/rust/issues/48427#issuecomment-367676264:
I think the big problem would be crate A instantiating static
aby invoking crate B'sxfunction. If then crateCalso instantiates staticaby invoking crateB'sxfunction, you end up with two differenta. So if crateDdepends onAandC, you'll get very weird behaviour. Might be solvable with MIR only rlibs, but it's generally really easy to get this wrong.Note that you can get around this limitation by creating another trait bound on
T:trait Foo: 'static { fn singleton() -> &'static Self { unimplemented!() } } fn x<T: Default + Foo>() { let a = T::singleton(); }Side-note: you won't be able to call
T::default()in a static constructor anyway
From #48427, the same behavior is seen for
statics
@estebank It's the same for all nested items really; STV is the solution to all of them except for maybe impl items.
Is there any news about this issue? Is it included in any roadmap? Can I do something to speed up its implementation? Especially for this case:
fn x<T: Default>() {
static a: T = T::default();
}
Thanks.
There has been no movement in supporting this at the lang level, and the diagnostic could do with some love to be clearer.
I also ran into this issue when I tried to do some compile-time assertions based on generic parameters from outer function. Here's one workaround I thought of, which I hope will help:
fn foo<T>() {
// can't use generic parameters from outer function:
// const ASSERT: () = assert!(mem::size_of::<T>() == 8);
let _ = Assert::<T>::SIZE_EQ_8;
}
struct Assert<T> {
_marker: PhantomData<T>,
}
impl<T> Assert<T> {
const SIZE_EQ_8: () = assert!(mem::size_of::<T>() == 8);
}
I can confirm that sometimes it leads to very strange compilation errors. This code compiles fine:
use core::marker::PhantomData;
struct A<T>(PhantomData<T>);
impl<T> A<T> {
const EMPTY: Vec<T> = Vec::new();
fn f() -> [Vec<T>; 2] {
[Self::EMPTY; 2]
}
}
fn main() {
let empty = A::<u32>::f();
dbg!(empty);
}
while this does not:
fn f<U>() -> [Vec<U>; 2] {
const EMPTY: Vec<U> = Vec::new();
[EMPTY; 2]
}
fn main() {
let empty = f::<u32>();
dbg!(empty);
}
@Ternvein, that is expected. While free constant items may not reference the generics of the outer item, associated constant items are allowed to reference the generics of their direct parent (i.e., trait, trait impl or impl).