【问题标题】:Returning and using a generic type with match返回并使用带有匹配的泛型类型
【发布时间】:2015-04-19 19:36:44
【问题描述】:

我正在开发一个简单的 Rust 应用程序,它接受标准输入并在此基础上运行。我想让每个命令都返回一个结果向量。

不同的命令可能返回不同类型的向量; list 方法返回一个PathBufs 的向量,但默认匹配臂返回字符串:

use std::{io, fs};
use std::path::PathBuf;

fn main() {
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed to read line");
    let chars_to_trim: &[char] = &['\n'];
    let trimmed_input: &str = input.trim_matches(chars_to_trim);

    let no_match = vec!["No Match"];

    let result = match trimmed_input {
        "list" => list(),
        _ => no_match,
    };
}

fn list() -> Vec<PathBuf> {
    println!("{}", "list of lockfiles here");
    let entries = fs::read_dir("/tmp").expect("Failed to read /tmp");
    let all: Result<_, _> = entries.map(|entry| entry.map(|e| e.path())).collect();
    all.expect("Unable to read an entry")
}

这会导致编译失败:

error[E0308]: match arms have incompatible types
  --> src/main.rs:12:22
   |
12 |         let result = match trimmed_input {
   |                      ^ expected struct `std::path::PathBuf`, found &str
   |
   = note: expected type `std::vec::Vec<std::path::PathBuf>`
   = note:    found type `std::vec::Vec<&str>`
note: match arm with an incompatible type
  --> src/main.rs:14:18
   |
14 |             _ => no_match,
   |                  ^^^^^^^^

处理这个问题的惯用 Rust 方法是什么?我已经阅读了有关泛型的文档,但我不确定如何应用它。

【问题讨论】:

    标签: rust


    【解决方案1】:

    这是一个简化的测试用例:

    use std::path::PathBuf;
    
    fn main() {
        let paths: Vec<PathBuf> = Vec::new();
        let defaults: Vec<&'static str> = Vec::new();
    
        let result = match 1 {
            1 => paths,
            _ => defaults,
        };
    }
    

    正如错误消息试图说的那样,Rust 要求单个变量始终具有相同的类型。让一个变量成为一组中的未知类型根本没有意义。

    您可以做的最直接的事情是创建一个 enum 来包装您拥有的两种情况:

    use std::path::PathBuf;
    
    enum MyThing<'a> {
        Str(&'a str),
        Path(PathBuf),
    }
    
    fn main() {
        let paths: Vec<PathBuf> = Vec::new();
        let defaults: Vec<&'static str> = Vec::new();
    
        let result: Vec<_> = match 1 {
            1 => paths.into_iter().map(MyThing::Path).collect(),
            _ => defaults.into_iter().map(MyThing::Str).collect(),
        };
    }
    

    你也可以选择一个中间立场并转换为那种类型,也许是String

    use std::path::PathBuf;
    
    fn main() {
        let paths: Vec<PathBuf> = Vec::new();
        let defaults: Vec<&'static str> = Vec::new();
    
        let result: Vec<_> = match 1 {
            1 => paths.into_iter().map(|p| p.to_string_lossy().into_owned()).collect(),
            _ => defaults.into_iter().map(|s| s.to_string()).collect(),
        };
    }
    

    第三种选择是创建一个 trait 并为这两种类型实现它。然后你可以创建一个trait 对象。此选项最接近您可能熟悉的动态语言。它增加了一个额外的间接层,允许更大的灵活性:

    use std::path::PathBuf;
    
    trait MyTrait {
        fn size(&self) -> u8;
    }
    
    impl MyTrait for PathBuf {
        fn size(&self) -> u8 {
            15
        }
    }
    
    impl<'a> MyTrait for &'a str {
        fn size(&self) -> u8 {
            88
        }
    }
    
    fn main() {
        let paths: Vec<PathBuf> = Vec::new();
        let defaults: Vec<&'static str> = Vec::new();
    
        let result: Vec<_> = match 1 {
            1 => paths.into_iter().map(|p| Box::new(p) as Box<MyTrait>).collect(),
            _ => defaults.into_iter().map(|s| Box::new(s) as Box<MyTrait>).collect(),
        };
    }
    

    【讨论】:

    • 感谢@Shepmaster。 Vec中的是什么意思?
    • &amp;'static strVec中包含的元素类型
    猜你喜欢
    • 1970-01-01
    • 2016-10-29
    • 1970-01-01
    • 1970-01-01
    • 2015-09-21
    • 1970-01-01
    • 1970-01-01
    • 2014-08-25
    • 1970-01-01
    相关资源
    最近更新 更多