简体   繁体   English

将泛型方法转换为 trait object 安全方法

[英]Turn generic method into trait object safe method

I would like to make an adapter that removes generic parameters (to produce a trait object) as in the example below.我想制作一个删除通用参数(以生成特征对象)的适配器,如下例所示。

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>>>),
) { }

Is there a way to accomplish this without a heap allocation, using only safe code, and no external dependencies?有没有办法在没有堆分配、只使用安全代码且没有外部依赖项的情况下完成此任务?

The below is an idea of what I want, but the borrow checker doesn't allow this.以下是我想要的想法,但借用检查器不允许这样做。

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>)>),
) { }

One simple way to do it is to require that the input iterator return references.一种简单的方法是要求输入迭代器返回引用。 This compiles:这编译:

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>)>),
) { }

Note that it's good style to have iterator adapters return iterators that can be further manipulated:请注意,让迭代器适配器返回可以进一步操作的迭代器是一种很好的方式:

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 _
    })
}

(You could also define this as a generic struct that implements the Iterator trait.) (您也可以将其定义为实现Iterator特征的通用结构。)

Now: if you don't want to require that the input iterator return references, then there's no way to return your new iterator.现在:如果您不想要求输入迭代器返回引用,那么就没有办法返回您的新迭代器。

What you're doing in the example code is creating a little buffer within your iterator, and returning references to it.您在示例代码中所做的是在迭代器中创建一个小缓冲区,并返回对它的引用。

If that buffer is stored in your iterator struct, what you're trying to create is called a streaming iterator and can't be implemented currently.如果该缓冲区存储在您的迭代器结构中,则您尝试创建的内容称为流式迭代器,目前无法实现。 This extremely long blog post explains why; 这篇极长的博文解释了原因; essentially, it would require large and hairy extensions to Rust's type system.本质上,它需要对 Rust 的类型系统进行大量而繁琐的扩展。

You can do something like this, if you rearrange your code to have users pass closures into your functions.如果您重新排列代码以让用户将闭包传递给您的函数,您可以这样做。 Then you control when the closures get called, so you can store the returned values into a buffer and pass references to the buffer into the closure.然后,您可以控制何时调用闭包,这样您就可以将返回的值存储到缓冲区中,并将对缓冲区的引用传递给闭包。 But that's not as ergonomic as the usual Iterator interface.但这不像通常的Iterator界面那样符合人体工程学。

This is sort of what you've done with your example code... I'm not exactly sure why it doesn't work.这就是您对示例代码所做的事情......我不确定为什么它不起作用。 If you changed take_dyn to take a single &'b (dyn 'b + Deref<Target = u8>)> and called it repeatedly it should work though.如果您将take_dyn更改为采用单个&'b (dyn 'b + Deref<Target = u8>)>并反复调用它,它应该可以工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM