在实现trait时出现了“期望类型参数,但发现结构体”的错误提示。

4
我正在尝试为有向图结构创建一个trait,并提供一个非常基本的实现,但遇到了编译器错误:
pub trait DiGraph<'a> {
    type N;
    fn nodes<T>(&'a self) -> T where T: Iterator<Item=&'a Self::N>;
    fn pre<T>(&'a self, node: Self::N) -> T where T: Iterator<Item=&'a Self::N>;
    fn succ<T>(&'a self, node: Self::N) -> T where T: Iterator<Item=&'a Self::N>;
}

struct SimpleNode {
    pre: Vec<usize>,
    succ: Vec<usize>,
}

pub struct SimpleDiGraph {
    pub nodes: Vec<SimpleNode>
}

impl<'a> DiGraph<'a> for SimpleDiGraph {
    type N = usize;

    fn nodes<T=std::ops::Range<usize>>(&'a self) -> T {
        return std::ops::Range { start: 0, end: self.nodes.len() };
    }
    fn pre<T=std::slice::Iter<'a,usize>>(&'a self, node: usize) -> T {
        return self.nodes[node].pre.iter();
    }
    fn succ<T=std::slice::Iter<'a,usize>>(&'a self, node: usize) -> T {
        return self.nodes[node].succ.iter();
    }
}

错误信息如下:
error[E0308]: mismatched types
--> digraph.rs:21:16
|
21 |         return std::ops::Range { start: 0, end: self.nodes.len() };
|                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found struct `std::ops::Range`
|
= note: expected type `T`
= note:    found type `std::ops::Range<usize>`

error[E0308]: mismatched types
--> digraph.rs:24:16
|
24 |         return self.nodes[node].pre.iter();
|                ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found struct `std::slice::Iter`
|
= note: expected type `T`
= note:    found type `std::slice::Iter<'_, usize>`

error[E0308]: mismatched types
--> digraph.rs:27:16
|
27 |         return self.nodes[node].succ.iter();
|                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found struct `std::slice::Iter`
|
= note: expected type `T`
= note:    found type `std::slice::Iter<'_, usize>`

错误信息对我来说有些令人困惑 - 为什么需要类型参数作为返回值?这是简单的类型不匹配(例如由于生命周期)导致的误导性错误消息吗?
1个回答

8

<T=std::ops::Range<usize>>并不会强制T成为std::ops::Range<usize>,它只是在不知道应该使用什么类型的情况下默认使用它。

如果你只想返回一个Range<usize>,那么将Range<usize>作为返回类型即可;根本没有必要使用泛型参数。你的代码现在实际上的意思是这样的:

  • “你可以选择任何你想要的返回类型!如果你不介意,我会返回一个range<usize>。”
  • “请返回一个String。”
  • “很遗憾,你将得到一个range<usize>!”
  • “...但是你说过...”
  • “我撒谎了!MUAHAHAHAHAHA!”

如果你实际上希望调用者选择返回类型,则需要准备返回任何T……这几乎是不可能的,因为它可以是从()String再到OpenGL渲染上下文的任何东西。

在这种情况下,您实际上想要做的是将T约束为某种需要类型实现某种构造函数的特征。 Default就是一个例子。

编辑:只是再次澄清:不能选择泛型参数中使用的类型,你的调用者可以。

我刚注意到,您在特征和实现中使用了不同的定义(不要那样做)。 我假设您实际上想要做的是说“这个方法返回可用作Iterator任何东西,但每个impl可以选择不同的类型。” 您无法通过泛型来实现此目标。

想要的是特征上的关联类型,如下所示:

pub trait DiGraph<'a> {
    type Nodes;
    fn nodes(&'a self) -> Self::Nodes;
}

pub struct SimpleNode {
    pre: Vec<usize>,
    succ: Vec<usize>,
}

pub struct SimpleDiGraph {
    pub nodes: Vec<SimpleNode>
}

impl<'a> DiGraph<'a> for SimpleDiGraph {
    type Nodes = std::ops::Range<usize>;

    fn nodes(&'a self) -> Self::Nodes {
        return std::ops::Range { start: 0, end: self.nodes.len() };
    }
}

谢谢,解释得很好,你猜对了我的意图。然而,在你的情况下,Nodes 可以是任何东西,这意味着任何在 DiGraph 上操作的函数都不能做太多事情,因为它不能对 Nodes 做出任何假设。所以我想做的是限制 Nodes,以便获得 N(在我的示例中为 usize,但由调用者定义)上的迭代器。我很高兴 SimpleDiGraph 总是返回 Range<N> 对象,但实现 DiGraph 的其他结构应该能够返回其他 N 上的迭代器。有什么办法可以实现这一点吗? - Henning Koehler
我想我找到了答案,似乎你可以限制 trait 的类型,比如: type NodeIterator: Iterator<Item=&'a Self::Node>; 这应该能解决问题。现在只需要解决生命周期的问题了。 - Henning Koehler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接