【问题标题】:Turn generic method into trait object safe method将泛型方法转换为 trait 对象安全方法
【发布时间】:2020-07-12 21:49:21
【问题描述】:

我想制作一个适配器来删除通用参数(以生成特征对象),如下例所示。

use std::ops::Deref;

fn make_dyn_box<I, S>(iter_in: I)
where
    I: Iterator<Item = S>,
    S: Deref<Target = u8>,
{
    let mut iter_out = iter_in.map(
        |s| -> Box<dyn Deref<Target = u8>> {Box::new(s)}
    );
    take_dyn_box(&mut iter_out)
}

fn take_dyn_box<'a: 'b, 'b>(
    iter: &'a mut (dyn 'a + Iterator<Item = Box<dyn 'b + Deref<Target = u8>>>),
) { }

有没有办法在不分配堆、只使用安全代码、不使用外部依赖项的情况下完成此任务?

以下是我想要的想法,但借用检查器不允许这样做。

use std::ops::Deref;

fn make_dyn<I, S>(iter_in: I)
where
    I: Iterator<Item = S>,
    S: Deref<Target = u8>,
{
    let mut item = None;
    let item = &mut item;
    let mut iter_out = iter_in.map(|s| -> &dyn Deref<Target = u8> {
        item.replace(s);
        Option::as_ref(item).unwrap()
    });
    take_dyn(&mut iter_out)
}

fn take_dyn<'a: 'b, 'b>(
    iter: &'a mut (dyn 'a + Iterator<Item = &'b (dyn 'b + Deref<Target = u8>)>),
) { }

【问题讨论】:

  • 当您说“适配器”时,您是指特指迭代器适配器吗?
  • 如果你可以使用一个引用迭代器,这个becomes very easy。但是,如果您有一个迭代器拥有它所迭代的值(或者更准确地说,产生拥有的值),您可能必须创建一个自定义迭代器,以某种方式至少存储每个产生的值,但产生特征对象(作为参考)。

标签: generics rust trait-objects


【解决方案1】:

一种简单的方法是要求输入迭代器返回引用。这样编译:

fn make_dyn<'b, I, S>(iter_in: I)
where
    I: Iterator<Item = &'b S>,
    S: Deref<Target = u8> + 'b,
{
    let mut iter_out = iter_in.map(|s| -> &dyn Deref<Target = u8> {
        s as _
    });
    
    take_dyn(&mut iter_out)
}

fn take_dyn<'a: 'b, 'b>(
    _iter: &'a mut (dyn 'a + Iterator<Item = &'b (dyn 'b + Deref<Target = u8>)>),
) { }

请注意,让迭代器适配器返回可以进一步操作的迭代器是一种很好的方式:

fn make_dyn_<'b, I, S>(iter_in: I) -> impl Iterator<Item = &'b (dyn 'b + Deref<Target = u8>)>
where
    I: Iterator<Item = &'b S>,
    S: Deref<Target = u8> + 'b,
{
    iter_in.map(|s| -> &dyn Deref<Target = u8> {
        s as _
    })
}

(您也可以将其定义为实现 Iterator 特征的通用结构。)

现在:如果您不想要求输入迭代器返回引用,那么就没有办法返回您的新迭代器。

您在示例代码中所做的是在迭代器中创建一个小缓冲区,并返回对它的引用。

如果该缓冲区存储在您的迭代器结构中,那么您尝试创建的内容称为流式迭代器,目前无法实现。 This extremely long blog post 解释原因;本质上,它需要对 Rust 的类型系统进行大量而繁琐的扩展。

如果您重新排列代码以让用户将闭包传递给您的函数,您可以执行类似的操作。然后,您可以控制何时调用闭包,这样您就可以将返回的值存储到缓冲区中,并将对缓冲区的引用传递给闭包。但这不像通常的Iterator 界面那样符合人体工程学。

这就是您对示例代码所做的事情...我不确定为什么它不起作用。如果您将 take_dyn 更改为单个 &amp;'b (dyn 'b + Deref&lt;Target = u8&gt;)&gt; 并反复调用它,它应该可以工作。

【讨论】:

  • 花了一些时间来阅读那篇博文,但感谢您提供背景信息;这很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多