【问题标题】:Cannot infer type for type parameter T, when the type is explicitly specified in a struct definition当类型在结构定义中明确指定时,无法推断类型参数 T 的类型
【发布时间】:2020-02-29 20:57:25
【问题描述】:

我有一个结构定义,其中包括此字段:

pub struct Separated<'a, I, T>
{
    ..., // other fields,
    separated: NonNull<dyn 'a + Iterator<Item = T>>,
}

不久之后,在其构造函数中,我尝试将该字段初始化为悬空指针:

let sep = Separated {
    ..., // other fields
    separated: NonNull::dangling(),
};

奇怪的是,这会产生这个错误:

error[E0282]: type annotations needed
   |
16 |             separated: NonNull::dangling(),
   |                        ^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T`

这个领域没有什么神秘之处;它的类型在结构定义中明确设置。我不明白为什么类型推断器不能推断出合适的类型来注入。

可以在下面找到产生此错误的最少 20 行示例和on the playground

use std::pin::Pin;
use std::ptr::NonNull;

pub struct Separated<'a, T> {
    t: &'a T,
    separated: NonNull<dyn 'a + Iterator<Item = T>>,
}

impl<'a, T> Separated<'a, T>
where
    T: 'a + Copy + PartialEq,
{
    fn new(t: &'a T) -> Pin<Box<Self>> {
        let sep = Separated {
            t,
            separated: NonNull::dangling(),
        };
        unimplemented!()
    }
}

我确实需要 separated 作为指向 trait 对象而不是单态类型的指针:它将包含的真正 trait 对象由一堆迭代器组合器组成,包括像 MapTakeWhile 这样的迭代器组合器,其类型包括函数指针,因此无法命名。

NonNull::dangling 不是参数函数:NonNull&lt;T&gt; 结构是参数的,但这个函数不是。因此,我不能只是想方设法摆脱困境。我完全不确定如何提供类型注释。


上下文,如果有用的话:我走这条路的全部原因是我试图创建一个迭代器组合器,为所有适当的迭代器自动实现,它在源迭代器的每个 N 元素之间注入一个元素。对于单个迭代器来说实现起来并不难,但作为一个通用组合器就更难了,因为由 Itertools 的chunks() 组合器生成的IntoChunks 结构本身并不是一个迭代器,只是一个实现IntoIterator 的结构。因此,我们需要跟踪IntoChunks 结构以及它产生的迭代器。

我采用的方法是创建一个自引用结构Separated,它包含这两者。假设结构总是固定的,这应该是安全的。然后我impl Iterator for Separated 并将next 调用推迟到self.separated

【问题讨论】:

    标签: pointers rust non-nullable


    【解决方案1】:

    根据标准文档,NonNull::dangling() 的定义是这样的:

    impl<T> NonNull<T> {
      pub const fn dangling() -> NonNull<T> {
        /* ... */
      }
    }
    

    在你的代码中,你在一个NonNull&lt;dyn 'a + Iterator&lt;Item = T&gt;&gt;类型的表达式中使用它,所以返回值必须是这个类型。

    这里微妙的是泛型类型参数有一个隐式的Sized 界限(除非它有一个?Sized 界限)。所以因为NonNull::dangling的实现没有?Sized绑定,Rust会尝试根据这些要求推断NonNull的类型参数:

    • 因为NonNull::&lt;T&gt;::dangling()方法没有绑定T: ?Sized,所以只对有大小的类型T实现,类型参数必须有大小。
    • 类型参数必须是dyn 'a + Iterator&lt;Item = T&gt;

    但是,由于 trait 对象(“dyn Trait 类型”)没有大小,Rust 不可能同时满足这两个要求,因此它“无法推断类型参数 T 的类型”。


    事实上,通过将类型显式添加到您的 Playground 示例中,您将收到一条更明确地说明问题的不同错误消息:

    let sep = Separated::<'a, T> {
      t,
      separated: NonNull::<dyn 'a + Iterator<Item = T>>::dangling(),
    };
    
    error[E0599]: no function or associated item named `dangling` found for type `std::ptr::NonNull<(dyn std::iter::Iterator<Item = T> + 'a)>` in the current scope
      --> src/lib.rs:16:64
       |
    16 |             separated: NonNull::<dyn 'a + Iterator<Item = T>>::dangling(),
       |                                                                ^^^^^^^^ function or associated item not found in `std::ptr::NonNull<(dyn std::iter::Iterator<Item = T> + 'a)>`
       |
       = note: the method `dangling` exists but the following trait bounds were not satisfied:
               `dyn std::iter::Iterator<Item = T> : std::marker::Sized`
    

    【讨论】:

    • 谢谢,这非常有用。不幸的是,我不能说它有帮助,因为它并没有让我更接近一个有效的实现。添加+?Sized 绑定不起作用;这些在特征对象中是不允许的。我真的只能用原始指针来完成吗?
    • 没关系,经过一番思考,您确实引导我找到了正确的答案:我只需要创建一个适当类型的临时函数指针(在本例中为let temporary_iter: *mut _ = &amp;mut std::iter::once(t)),并初始化@ 987654340@ 和 NonNull::new_unchecked确实适用于 ?Sized
    • @coriolinus NonNull 实现了CoerceUnsized,这意味着您可以执行NonNull::&lt;std::iter::Empty&lt;T&gt;&gt;::dangling() 之类的操作,编译器会将NonNull&lt;Empty&lt;T&gt;&gt; 强制转换为NonNull&lt;dyn 'a + Iterator&lt;Item = T&gt;&gt;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多