Reputation: 95
Is this intended behaviour or a compiler bug?
The following code does not compile. values
in MyStruct
is an Option
since Vec::new
is not a const fn - but Option::None
is constant (but it still does not compile).
type MyFun = fn(input: u32) -> u32;
struct MyStruct {
values: Option<Vec<MyFun>>,
}
impl MyStruct {
pub const fn init() -> Self {
Self { values: None }
}
}
fn main() {
MyStruct::init();
}
error[E0723]: function pointers in const fn are unstable
--> src/main.rs:9:24
|
9 | Self { values: None }
| ^^^^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
= help: add `#![feature(const_fn)]` to the crate attributes to enable
Using a newtype (Wrapped
) solves the problem. This feels strange, since both examples are identical (at least the generated binaries should be identical). Is this behaviour intended or is that a bug?
This works:
type MyFun = fn(input: u32) -> u32;
struct Wrapped(Vec<MyFun>);
struct MyStruct {
values: Option<Wrapped>,
}
impl MyStruct {
pub const fn init() -> Self {
Self { values: None }
}
}
fn main() {
MyStruct::init();
}
Upvotes: 2
Views: 1323
Reputation: 31263
TLDR: A bug, but not the one you were hinting at. We are overaggressive in keeping the door open for future features in const fn.
The error you are encountering was created in order to prevent users from writing functions like
const fn foo(f: fn()) {}
since it is impossible to use f
in a callable way. The following function is illegal right now.
const fn foo(f: fn()) { f() }
At the time of stabilization of const fn
we weren't sure whether it should be legal, so we preemptively forbade fn
pointers in const fn
. The same goes for creating function pointers within the const fn. So
const fn foo() {
let f: fn() = some_function;
}
is forbidden, because we want to keep the door open for permitting
const fn foo() {
let f: fn() = some_function;
f();
}
If you expand your use case's code, you get something like
pub const fn init() -> Self {
let values: Option<fn(u32) -> u32> = None;
Self { values }
}
You are correct in that this is a bug. Though not due the difference between the wrapped and non-wrapped version, but because the None
never actually creates a function pointer. We just decided to be overly aggressive in the check in order to not miss anything. It is definitely possible to create a better analysis here, though we may just want to implement function pointers within const fn.
The reason there's a difference between the wrapped and non-wrapped version is that we want to allow
const fn foo(f: fn()) { f() }
but not
const fn foo(wrapper: Wrapper) { (wrapper.f)() }
as the latter doesn't show the function pointer in the API, so we can't do any magic to ensure that the function pointer points to a const fn.
The difference between the wrapped and non-wrapped version may be confusing enough to force us to abandon the idea that we can just invoke fn
pointers in const fn as described above and come up with a new scheme for fn
pointers in const fn.
Upvotes: 6