При определении API для обратных вызовов обработчика (например, обработка команд оболочки или сетевых запросов) я хотел бы скрыть подробности реализации от сигнатуры обратного вызова - например, я хотел бы принять обратные вызовы вида
fn c(data: impl Iterator<Item = i32>) -> ()
Хотя я легко могу выразить сами обратные вызовы, используя синтаксис аргумента impl Trait
или как fn c<I: Iterator<...>>(data: I) -> ()
, я не могу принять их в общем, потому что мой процессор не универсален на I
, а экзистенциальн.
Я мог бы сделать общие части обработки на I
, а затем сказать, что I: Iterator<Item = i32>
:
/// A non-working callback that receives an iterator.
use std::iter::*;
use std::marker::PhantomData;
struct Processor<CB, I> {
callback: CB,
_i: PhantomData<I>,
}
// Here it'd be nice to say that it won't implement it for all I, but there exists an (unnamable) I
// for which it's implemented.
impl<CB, I> Processor<CB, I>
where
CB: FnMut(I) -> (),
I: Iterator<Item = i32>,
{
fn process(self) {
let a = [23, 42].iter().map(|i| i + 1);
let mut cb = self.callback;
cb(a)
}
}
fn c(data: impl Iterator<Item = i32>) {
println!("Data:");
for i in data {
println!("Item: {}", i);
}
}
fn main() {
let p = Processor {
callback: c,
_i: PhantomData,
};
p.process()
}
детская площадка
Это не такпоскольку реализация не является общей для I
, и компилятор выражает свое недовольство ложью:
error[E0308]: mismatched types
--> src/main.rs:20:12
|
20 | cb(a)
| ^ expected type parameter, found struct `std::iter::Map`
|
= note: expected type `I`
found type `std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:18:37: 18:46]>`
Я также попытался назвать как можно больше имен без имен, но это генерирует действительно громоздкие имена типов (работоспособныев примере, но в результате получаются многострочные имена типов в практическом коде):
/// A non-working callback that receives an iterator.
use std::iter::*;
struct Processor<CB> {
callback: CB,
}
impl<CB> Processor<CB>
where
CB: FnMut(Map<std::slice::Iter<'static, i32>, FnOnce(i32) -> i32>) -> (),
{
fn process(self) {
let a = [23, 42].iter().map(|i| i + 1);
let mut cb = self.callback;
cb(a)
}
}
fn c(data: impl Iterator<Item = i32>) {
println!("Data:");
for i in data {
println!("Item: {}", i);
}
}
fn main() {
let p = Processor { callback: c };
p.process()
}
детская площадка
Это все равно не работает, еслиЗдесь задействован лямбда-тип:
error[E0277]: the size for values of type `(dyn std::ops::FnOnce(i32) -> i32 + 'static)` cannot be known at compilation time
--> src/main.rs:8:1
|
8 | / impl<CB> Processor<CB>
9 | | where
10 | | CB: FnMut(Map<std::slice::Iter<'static, i32>, FnOnce(i32) -> i32>) -> (),
11 | | {
... |
16 | | }
17 | | }
| |_^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::FnOnce(i32) -> i32 + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-sized>
= note: required by `std::iter::Map`
error[E0277]: the size for values of type `(dyn std::ops::FnOnce(i32) -> i32 + 'static)` cannot be known at compilation time
--> src/main.rs:12:5
|
12 | / fn process(self) {
13 | | let a = [23, 42].iter().map(|i| i + 1);
14 | | let mut cb = self.callback;
15 | | cb(a)
16 | | }
| |_____^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::FnOnce(i32) -> i32 + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-sized>
= note: required by `std::iter::Map`
Какой идиоматический способ обойти это?Возможно ли с другим синтаксисом, который говорит: «Для всех CB
это реализует Processor
, где тип CB
должен быть универсальным в своем аргументе, и я могу выбрать его тип»?Что-то вроде
impl<CB> Processor<CB>
where
CB: FnMut(impl Iterator<Item = i32>) -> (),
Учитывая, что я стремлюсь к встраиваемой среде, я ищу решения, не использующие стандартное управление, поэтому такие подходы, как Box<...>
, не решат проблему.