Есть ли способ сохранить указатель любой функции в структуре?

Мне было интересно, есть ли способ получить указатель функции с неопределенными аргументами и типами вывода (неопределение типа вывода не так необходимо, как аргументы). Например, в C++, когда вы создаете новый поток, вы передаете указатель на функцию без какой-либо спецификации.

Как я вижу, это можно решить с помощью типажа, который будет представлять fn так же, как std::marker::Tuple представляет Tuple объект.

Пример:

struct Cont <F: FN_TRAIT> {
    func: F,
}

impl<F: FN_TRAIT> Cont <F> {
    fn new(func: F) -> Self {
        Self { func: func }
    }

    fn start(&self, args: dyn std::marker::Tuple) {
        self.func.call(args);
    }
}

fn temp(a: i32, b: i32) {
    println!("{}", a + b);
}

fn main() {
    let a = Cont::new(temp);
    a.start((0,1));
}

Я попытался найти способ представить fn с помощью типажа в структуре, а затем вызвать его с помощью std::marker::Tuple в качестве аргументов.

Что бы вы хотели сделать с сохраненным указателем? Поскольку вы не знаете, какие аргументы он принимает, вы не сможете его вызвать. В большинстве случаев вы можете вместо этого сохранить Fn(), и если вам нужно передать аргументы функции, вы передаете замыкание, например move || foo(x, y, z).

Sven Marnach 17.05.2023 08:27

Обратите внимание, что Tuple — это элемент языка (т. е. он встроен в компилятор). У него также нет никакого поведения, поэтому знание того, что тип реализует Tuple, не позволяет вам вообще ничего с ним делать. Черта Tuple полезна только для других вещей, встроенных в компилятор, например. для ограничения параметра Args признака Fn.

Sven Marnach 17.05.2023 08:37

Весь смысл был в том, чтобы полагаться на компилятор Rust. Fn в Rust уже может получить любую функцию и вызвать ее с любым переданным кортежем std::ops::Fn::call(&|name_of_function|, |name_of_tuple|);, а компилятор сам распознает, что мы передали функцию, и проверяет, соответствует ли переданный кортеж потребностям функции.

Michael Smoliar 17.05.2023 09:43

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

Michael Smoliar 17.05.2023 09:49
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете сделать это с помощью дженериков в основном так же, как это делает С++:

struct Cont<Args, F> {
    args: Args,
    fun: F,
}

impl<Args, F> Cont<Args, F> {
    fn new (fun: F, args: Args) -> Self {
        Cont { args, fun, }
    }
}

impl<A, R, F: FnOnce (A) -> R> Cont<(A,), F> {
    fn call (self) -> R {
        (self.fun) (self.args.0)
    }
}

impl<A0, A1, R, F: FnOnce (A0, A1) -> R> Cont<(A0, A1), F> {
    fn call (self) -> R {
        (self.fun) (self.args.0, self.args.1)
    }
}

fn main() {
    let c1 = Cont::new (|x| 2*x, (0,));
    let c2 = Cont::new (|x, y| x+y, (2, 3));
    println!("{} {}", c1.call(), c2.call());
}

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

За исключением того, что вам нужно добавить реализацию для каждого возможного количества аргументов (это, вероятно, можно упростить с помощью макроса).

В чем преимущество использования этого перед замыканиями без аргументов?

Sven Marnach 17.05.2023 12:10

Другие вопросы по теме