Почему я получаю сообщение об ошибке «указатели на функции в const fn нестабильны», но оно исчезает при переносе в новый тип? - PullRequest
1 голос
/ 18 июня 2020

Это предполагаемое поведение или ошибка компилятора?

Следующий код не компилируется. values в MyStruct - это Option, поскольку Vec::new не является константной функцией, но Option::None является константой (но все равно не компилируется).

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

Использование нового типа (Wrapped) решает проблему. Это кажется странным, поскольку оба примера идентичны (по крайней мере, сгенерированные двоичные файлы должны быть идентичными). Это поведение задумано или это ошибка?

Это работает:

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();
}

Детская площадка

1 Ответ

4 голосов
/ 18 июня 2020

TL; DR: ошибка, но не та, на которую вы намекали. Мы чересчур агрессивно держим дверь открытой для будущих функций в const fn.

Ошибка, с которой вы сталкиваетесь, была создана для того, чтобы пользователи не могли писать такие функции, как

const fn foo(f: fn()) {}

, поскольку это невозможно использовать f вызываемым способом. Следующая функция сейчас недопустима.

const fn foo(f: fn()) { f() }

Во время стабилизации const fn мы не были уверены, должна ли она быть допустимой, поэтому мы превентивно запретили указатели fn в const fn. То же самое касается создания указателей на функции внутри const fn. Итак,

const fn foo() {
    let f: fn() = some_function;
}

запрещено, потому что мы хотим оставить дверь открытой для разрешения

const fn foo() {
    let f: fn() = some_function;
    f();
}

Если вы расширите код своего варианта использования, вы получите что-то вроде

pub const fn init() -> Self {
    let values: Option<fn(u32) -> u32> = None;
    Self { values }
}

Вы правы в том, что это ошибка. Хотя не из-за разницы между версией с оболочкой и без оболочки, а потому, что None на самом деле никогда не создает указатель на функцию. Мы просто решили действовать чересчур агрессивно, чтобы ничего не упустить. Здесь определенно можно провести лучший анализ, хотя мы можем просто реализовать указатели на функции в const fn.

Причина, по которой существует разница между версией с оболочкой и без оболочки, заключается в том, что мы хотим разрешить

const fn foo(f: fn()) { f() }

, но не

const fn foo(wrapper: Wrapper) { (wrapper.f)() }

, поскольку последний не показывает указатель на функцию в API, поэтому мы не можем делать никаких магий c, чтобы гарантировать, что указатель на функцию указывает на const fn.

Разница между обернутой и не обернутой версией может быть достаточно запутанной, чтобы заставить нас отказаться от идеи, что мы можем просто вызвать fn указатели в const fn, как описано выше, и прийти вверх с новой схемой для fn указателей в const fn.

...