返回和使用匹配的泛型类型 [英] Returning and using a generic type with match

查看:39
本文介绍了返回和使用匹配的泛型类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个简单的 Rust 应用程序,它接受标准输入并在其基础上进行操作.我希望每个命令都返回一个结果向量.

I'm working on a simple Rust app that accepts stdin and acts on the basis of it. I'd like to have each command return a vector of results.

不同的命令可能返回不同类型的向量;list 方法返回一个由 PathBuf 组成的向量,但默认的 match arm 返回字符串:

Different commands may return differently typed vectors; the list method returns a vector of PathBufs, but the default match arm returns strings:

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 方法是什么?我已经通读了关于泛型的文档,但我不知道如何应用它.

What is the idiomatic Rust way to handle this? I've read through the documentation on generics but I'm not sure how to apply it.

推荐答案

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

Here's a reduced testcase:

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 要求单个变量始终具有相同的类型.让一个变量成为集合中的未知类型是没有意义的.

As the error message is trying to say, Rust requires that a single variable will always have the same type. It simply doesn't make sense to have one variable be an unknown type out of a set.

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

The most straight-forward thing you can do is create an enum that wraps both cases you have:

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:

You could also just pick a middle ground and convert to that type, perhaps 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 并为这两种类型实现它.然后您可以创建一个特征对象.此选项最接近您可能熟悉的动态语言.它增加了一个额外的间接层,从而提供了更大的灵活性:

A third option is to create a trait and implement it for both types. You can then create a trait object. This option is closest to dynamic languages that you may be familiar with. It adds an extra layer of indirection, allowing for more flexibility:

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(),
    };
}

这篇关于返回和使用匹配的泛型类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆