Мне было интересно, есть ли способ получить указатель функции с неопределенными аргументами и типами вывода (неопределение типа вывода не так необходимо, как аргументы). Например, в 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 в качестве аргументов.
Обратите внимание, что Tuple — это элемент языка (т. е. он встроен в компилятор). У него также нет никакого поведения, поэтому знание того, что тип реализует Tuple, не позволяет вам вообще ничего с ним делать. Черта Tuple полезна только для других вещей, встроенных в компилятор, например. для ограничения параметра Args признака Fn.
Весь смысл был в том, чтобы полагаться на компилятор Rust. Fn в Rust уже может получить любую функцию и вызвать ее с любым переданным кортежем std::ops::Fn::call(&|name_of_function|, |name_of_tuple|);, а компилятор сам распознает, что мы передали функцию, и проверяет, соответствует ли переданный кортеж потребностям функции.
Переместить закрытие не работает в моем случае, потому что весь смысл в том, чтобы автоматически создать обертку для функции и ее аргументов, а не делать это вручную в замыкании.





Вы можете сделать это с помощью дженериков в основном так же, как это делает С++:
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());
}
За исключением того, что вам нужно добавить реализацию для каждого возможного количества аргументов (это, вероятно, можно упростить с помощью макроса).
В чем преимущество использования этого перед замыканиями без аргументов?
Что бы вы хотели сделать с сохраненным указателем? Поскольку вы не знаете, какие аргументы он принимает, вы не сможете его вызвать. В большинстве случаев вы можете вместо этого сохранить
Fn(), и если вам нужно передать аргументы функции, вы передаете замыкание, напримерmove || foo(x, y, z).