Подтвердить что ты не робот

Динамически выбирать функцию для вызова без промежуточных переменных

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

fn foo() {
    println! ("Foo");
}
fn bar() {
    println! ("Bar");
}

fn main() {
    let selector = 0;

    let foo: &Fn() = &foo;
    let bar: &Fn() = &bar;
    let test = match selector {
        0 => foo,
        _ => bar
    };
    test();
}

Мой вопрос: можно ли избавиться от промежуточных переменных? Я попробовал просто удалить их:

fn foo() {
    println! ("Foo");
}
fn bar() {
    println! ("Bar");
}

fn main() {
    let selector = 0;

    let test = match selector {
        0 => &foo as &Fn(),
        _ => &bar as &Fn()
    };
    test();
}

но затем контролер заемщиков жалуется, что заемные значения действительны только до конца матча (кстати, почему? функции 'static в любом случае так должны быть действительными до конца времен). Я также попытался сделать ядро ​​'static явным с помощью &foo as &'static Fn(), но это тоже не работает.

4b9b3361

Ответ 1

Следующее работает, если вам нужно работать только со статическими функциями, а не с закрытием:

fn foo() {
    println!("Foo");
}

fn bar() {
    println!("Bar");
}

fn main() {
    let selector = 0;

    let test: fn() = match selector {
        0 => foo,
        _ => bar
    };

    test();
}

(попробуйте игровая площадка)

Здесь я использовал тип функции вместо функции.

Причина, по которой объект заимствованного объекта не работает, вероятно, следующий. Любой объект-объект представляет собой толстый указатель, который состоит из указателя на какое-то значение и указателя на виртуальную таблицу. Когда объект-признак создается из замыкания, все становится ясным - значение будет представлено самим замыканием (внутренне являющимся экземпляром структуры, содержащей все захваченные переменные), и виртуальная таблица будет содержать указатель на реализацию соответствующий признак Fn*(), сгенерированный компилятором, чьим телом будет тело замыкания.

С функциями, однако, все не так ясно. Нет значения для создания объекта-объекта из-за того, что сама функция должна соответствовать реализации признака Fn(). Следовательно, rustc, вероятно, генерирует пустую структуру и реализует для нее Fn(), и эта реализация вызывает статическую функцию напрямую (не фактический Rust, а что-то близкое):

struct SomeGeneratedStructFoo;

impl Fn<()> for SomeGeneratedStructFoo {
    type Output = ();
    fn call(&self, args: ()) -> () {
        foo();
    }
}

Следовательно, когда объект-объект создается из fn foo(), ссылка фактически делается на временное значение типа SomeGeneratedStructFoo. Однако это значение создается внутри совпадения, и только ссылка на него возвращается из совпадения, поэтому это значение не проживает достаточно долго, и это то, о чем идет речь.

Ответ 2

fn() - тип указателя функции. Он уже тип указателя. Вы можете проверить это с помощью std::mem::size_of::<fn()>(). Это не тип нулевого размера.

Когда вы выполняете &foo, вы берете указатель на указатель, выделенный стекю. Этот внутренний указатель не выживает очень долго, вызывая ошибку.

Вы можете наложить их на общий тип fn(), как было предложено. Мне было бы интересно узнать, почему вы не можете использовать fn() до &Fn().