Comments 7
Формально, кстати, такая конструкция не является замыканием, так как в ней отсутствуют ссылки на переменные, объявленные вне тела этой функции
Формально следует смотреть только лексические ссылки, то есть использование идентификаторов из внешней области видимости. А приведёт ли это к созданию ссылки или к копированию/перемещению — вопрос реализации. Кстати, как раз замыкания с копированием (т.е. захватом по значению) считаются "более настоящими".
Возврат через impl не работает для интерфейсов [...] Вон оно что, данные замыкания возвращается через стек и вызывающая сторона должна подготовить место для этого (sub rsp, ...). Размер данных замыкания известен компилятору только если он имеет возможность "увидеть" что происходит внутри вызываемой функции.
Вы забыли про такую возможность как type alias, которая как раз и даёт возможность "посмотреть" внутрь возвращаемого типа:
trait Summer {
type Closure: FnMut(i32, i32) -> i32;
fn get_sum(mult: i32) -> Self::Closure;
}
Правда, реализовать такой типаж не так-то просто, и если нет желания раскрывать реализацию замыкания вручную — то самое "умное" что можно сделать — это вернуться к Box<dyn>
:
struct Foo;
impl Summer for Foo {
type Closure = Box<dyn FnMut(i32, i32) -> i32>;
fn get_sum(mult: i32) -> Self::Closure {
Box::new(move |x, y| {
mult * (x + y)
})
}
}
Но вот в "ночной" ветке доступна фича type_alias_impl_trait:
#![feature(type_alias_impl_trait)]
struct Foo;
impl Summer for Foo {
type Closure = impl FnMut(i32, i32) -> i32;
fn get_sum(mult: i32) -> Self::Closure {
move |x, y| {
mult * (x + y)
}
}
}
Вы забыли про такую возможность как type alias
Здесь не просто type alias, еще нужны associated types, которые требуют отдельного описания. Это в планах.
Правда, реализовать такой типаж не так-то просто
Если это возможно, приведите пример?
Я же их привёл, аж два примера.
После слов:
Правда, реализовать такой типаж не так-то просто, и если нет желания раскрывать реализацию замыкания вручную…
я ожидал, что таки в рамках Stable channel можно предложить сложную реализацию ("замыкания вручную"), которая возвращает замыкание не через кучу. Cобственно именно это и интересует.
Вы же предложили (для Stable channel) вернуться к Box+dyn. Ну т.е. таки "в кучу, товарищи".
В общем, смысл Вашего комментария по отношению к стабильной ветке (про что я, собственно, рассказываю) не очень понятен мне.
К слову, можно ведь и так определить тип, подающий надежды на то, что можно вернуть замыкание:
trait Summer<Closure: FnMut(i32, i32) -> i32> {
fn get_sum(mult: i32) -> Closure;
}
Но толку-то...
А, вот вы про что. Ну, никто же не мешает определить самому структуру Closure и реализовать нужный трейт, как вы это сами и показывали. Долго и нудно, но позволит избежать использования кучи в стабильной ветке.
Я показывал упрощенные модели оригинальных трейтов замыканий (FnOnce и т.д.), которые тоже не "настоящие" (как мне кажется). "Не настоящие" в том смысле, что они обрабатываются компилятором особым образом. Я считаю, что реализовать их самостоятельно нельзя, компилятор не поймет, ибо это его сугубо "внутреннее дело". А если и поймет, то нельзя будет вернуть, а если можно вернуть, то что-то еще пойдет не так.
Вполне могу ошибаться, и любопытно было бы увидеть пример, опровергающий эту точку зрения.
Замыкания в Rust