Создание структуры, которая имеет заменяемую функцию - PullRequest
1 голос
/ 22 сентября 2019

Я хотел бы иметь структуру, которая содержит функцию, установленную пользователем этой структуры.Я придумал что-то вроде этого:

pub struct MyStruct {
    some_function: Fn(f64) -> f64
}

Затем я хочу определить набор констант, которые можно выбрать при создании экземпляра MyStruct.Я попробовал это так:

pub const SIGMOID: Fn(f64) -> f64 = |x| 1.0 / (1.0 + E.powf(x));

Я получаю ошибку компиляции, что Fn(f64) -> f64 не имеет размера, известного во время компиляции, и предупреждение об использовании ключевого слова dyn.Я не хочу использовать ключевое слово dyn из-за снижения производительности.Должен ли я поместить эту константу в кучу, используя Box, или есть более легкий способ сделать это?Я думаю, что при сохранении в куче будет также и потеря производительности, или я здесь ошибаюсь?

Ответы [ 2 ]

3 голосов
/ 22 сентября 2019

Цитировать Fn черта документы :

Эта черта (Fn)не путать с функциональными указателями (fn).

Функциональные указатели не позволяют иметь состояние в виде замыкания.Если вы используете что-то вроде функции sigmoid, которая определена без состояния, вы можете использовать это.Если вы используете замыкание с состоянием, вам нужно , чтобы иметь некоторую форму динамической отправки или универсального типа, потому что вы не можете назвать тип замыкания.

Например, с помощью fn poitners:

struct Foo {
    my_func: fn(f64) -> f64
}

Или с динамической рассылкой и ссылками:

struct Foo<'a> {
    my_func: &'a dyn Fn(f64) -> f64,
}

или с изменяемым состоянием:

struct Foo<'a> {
    my_func: &'a mut dyn FnMut(f64) -> f64,
}

Без продолжительности жизни:

struct Foo {
   my_func: Box<dyn FnMut(f64) -> f64>
}

Я думаю, что при сохранении его в куче будет и снижение производительности, или я здесь ошибаюсь?

Да, частоштраф с динамической диспетчеризацией из-за двойной косвенности прохождения vtable. Я верю , я не уверен, есть также наказание из-за отсутствия мономорфизации.Но они оба не важны, если они не поддаются количественной оценке.

pub const SIGMOID: Fn(f64) -> f64 = |x| 1.0 / (1.0 + E.powf(x));

Это может стать

pub const SIGMOID: fn(f64) -> f64 = |x| 1.0 / (1.0 + E.powf(x));

И fn указателями реализовать все Fn* черты:

Кроме того, функциональные указатели любая подпись, ABI или безопасность являются Copy, и все безопасные функциональные указатели реализуют Fn, FnMut и FnOnce.Это работает, потому что эти черты специально известны компилятору.

2 голосов
/ 22 сентября 2019

Ключевое слово dyn не снижает производительности, если оно не используется.Неиспользование его считается неясным и не будет поддерживаться в будущей версии Rust.

Учитывая, что Fn(f64) -> f64 является признаком, любой тип может его реализовать, а любые две функции считаются разными типами.Если вы хотите быть достаточно гибкими, чтобы использовать любой из разработчиков этой черты, то вам понадобится для использования динамической диспетчеризации - я полагаю, это снижение производительности, которого вы хотите избежать.

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

pub struct MyStruct {
    someFunction: Box<dyn Fn(f64) -> f64>,
}

Если вы абсолютно не можете использовать динамическую диспетчеризацию, вы можете использовать указатель на функцию вместочерта объекта.Указатель на функцию - это особый тип (не признак), к которому можно привести элементы fn.Однако большинство замыканий нельзя привести к указателю на функцию, потому что им потребуется реализация Copy.По сути, это означает, что «замыкание» не закрывается ни над чем и могло быть так же легко записано как элемент функции.Это будет выглядеть так:

pub struct MyStruct {
    // Notice: lower case fn
    some_function: fn(f64) -> f64,
}

// Notice: lower case fn
pub const SIGMOID: fn(f64) -> f64 = |x| 1.0 / (1.0 + E.powf(x));

fn main() {
    let s = MyStruct {
        some_function: SIGMOID,
    };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...