为什么一旦我为Vec<T>添加实现,Rust编译器就不使用预期的trait实现?

7

我正在尝试实现一个读取器,它可以从文件中提取不同类型的值。有一个表示文件资源(和访问其内容的方法)的File结构体,以及一个Reader特质,它使得根据结果类型提取值成为可能。这个(虚拟的)实现看起来像这样(playground):

use std::io::Result;

mod file {
    use std::io::Result;

    pub struct File {/* ... */}

    pub trait Reader<T> {
        fn read(&mut self) -> Result<T>;
    }

    impl Reader<u32> for File {
        fn read(&mut self) -> Result<u32> {
            // Dummy implementation
            Ok(10)
        }
    }

    impl Reader<u8> for File {
        fn read(&mut self) -> Result<u8> {
            // Dummy implementation
            Ok(0)
        }
    }

    impl Reader<bool> for File {
        fn read(&mut self) -> Result<bool> {
            // Dummy implementation
            Ok(false)
        }
    }
}

use file::{File, Reader};

impl<T: Default> Reader<Vec<T>> for File
where
    File: Reader<T> + Reader<u32>,
{
    fn read(&mut self) -> Result<Vec<T>> {
        let count: u32 = self.read()?;
        let mut array: Vec<T> = Vec::with_capacity(count as usize);
        for _ in 0..count {
            let mut item: T = self.read()?;
            array.push(item);
        }

        Ok(array)
    }
}

fn main() {
    let mut file = File {};
    let _v: Vec<u8> = file.read().unwrap();
}

在我添加Reader<Vec<T>>实现之前,一切都正常。向量以u32的形式存储在文件中,表示元素数量,后跟元素的表示。编译器给出以下错误:

error[E0308]: try expression alternatives have incompatible types
  --> src/main.rs:41:26
   |
41 |         let count: u32 = self.read()?;
   |                          ^^^^^^^^^^^^
   |                          |
   |                          expected u32, found type parameter
   |                          help: try wrapping with a success variant: `Ok(self.read()?)`
   |
   = note: expected type `u32`
              found type `T`

尽管我已经指定了File实现了Reader<T>Reader<u32>,但它似乎仅使用Reader<T>。更奇怪的是,如果我只保留2个Reader trait的实现(例如删除Reader<bool>),则代码可以编译而无需任何问题(playground)。为什么编译器不能找到应该使用Reader<u32>实现进行count初始化?我应该做出什么改变?我已经找到了一种解决方法,但我仍然对为什么编译器无法自动解决此问题感兴趣:
let count: u32 = (self as &mut Reader<u32>).read()?;

问题已被报告为 rust-lang/rust#54344


1
我认为你应该将此报告为一个错误。 - Peter Hall
4
但我仍然对了解为什么编译器不能自动找出这个问题很感兴趣。黑魔法使用了很多法力,所以也许它已经用完了。 - Stargateur
@Stargateur 我觉得编译器应该能够解决这个问题。结果中的 T 与类型参数中的 T 是相同的,不可能有不同的实现具有相同的返回类型。 - Peter Hall
当你完成报告时,请在此处链接它(或在SO Rust聊天室中链接)。 - Peter Hall
我已经将这个作为问题报告并在问题中添加了一个参考。@Shepmaster 感谢您的提示,但是由于 read() 返回 Result,处理错误会使代码变得不太易读。 - Tey'
显示剩余3条评论
1个回答

2
编译器应该能够自动选择使用Reader<u32>实现来处理let count: u32 = ...。这是一个编译器的错误,因为Tself.read()在该行中的使用方式无关。当一个.read()调用的返回类型决定另一个.read()调用的返回类型时,这似乎是不应该发生的!此外,如果这不是一个错误,那么除了Reader<u8>Reader<u32>之外,其他Reader<T>的实现都没有区别。但正如@rodrigo所指出的,存在Reader<bool>实现会触发此错误。
请注意,?(等同于下面显示的match)与这个错误无关,因为即使直接获取Result<u32>,仍然会出现错误。
let count_result: Result<u32> = self.read(); // error happens here
let count: u32 = match count_result {
    std::result::Result::Ok(val) => val,
    std::result::Result::Err(err) => {
        return std::result::Result::Err(std::convert::From::from(err))
    }
};

这是一个编译器的错误,请链接到相关的错误报告以供将来参考。 - Shepmaster
那么 ? 变成了什么? - Aaron Miller
1
try! 可以在宏中看到? 被转换成对 Try trait 的调用。例如,try! 在使用 Option 时无效,而 ? 可以使用。 - Shepmaster
@Shepmaster 关于这个 bug 报告,我在 rust-lang/rust 的问题列表中进行了搜索,但没有找到任何相关内容。我认为还没有人报告过这个问题。 - Aaron Miller
1
@AaronMiller 感谢您的回答,我已经为您的努力点赞,并且它提醒我在 rust-lang/rust 中报告了这个问题(我刚刚完成了)。然而,我不确定我能否接受它作为解决方案,因为已经有其他人在评论中给出了错误原因(如果是编译器错误,则没有解决方案)。 - Tey'
@Tey' 这在 Stack Overflow 上是毫无意义的。评论被认为是非常短暂的,它们随时可以被删除。如果那些评论的作者想花时间提供答案,他们本来就可以(现在仍然可以)。当然,您可以继续不接受答案,但“因为有人在评论中说过”并不是 Stack Overflow 参与者关心的事情。 - Shepmaster

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