【问题标题】:How to translate "x-y" to vec![x, x+1, … y-1, y]?如何将“x-y”翻译成 vec![x, x+1, ... y-1, y]?
【发布时间】:2018-03-08 02:35:25
【问题描述】:

这个解决方案似乎相当不优雅:

fn parse_range(&self, string_value: &str) -> Vec<u8> {
    let values: Vec<u8> = string_value
        .splitn(2, "-")
        .map(|part| part.parse().ok().unwrap())
        .collect();

    { values[0]..(values[1] + 1) }.collect()
}
  • 由于splitn(2, "-") 为任何有效的string_value 返回两个结果,因此最好将元组直接分配给两个变量firstlast,而不是看似任意长度的Vec。我似乎无法用元组做到这一点。
  • collect() 有两个实例,我想知道它是否可以减少到一个(甚至为零)。

【问题讨论】:

  • 只要想回一个Vec&lt;u8&gt;,至少需要1个收藏

标签: rust readability


【解决方案1】:

简单的实现

fn parse_range(string_value: &str) -> Vec<u8> {
    let pos = string_value.find(|c| c == '-').expect("No valid string");
    let (first, second) = string_value.split_at(pos);

    let first: u8 = first.parse().expect("Not a number");
    let second: u8 = second[1..].parse().expect("Not a number");

    { first..second + 1 }.collect()
}

Playground

我建议返回 Result&lt;Vec&lt;u8&gt;, Error&gt; 而不是用 expect/unwrap 恐慌。

每晚实施

我的下一个想法是关于第二集。这是一个使用夜间代码的代码示例,但您根本不需要任何收集。

#![feature(conservative_impl_trait, inclusive_range_syntax)]

fn parse_range(string_value: &str) -> impl Iterator<Item = u8> {
    let pos = string_value.find(|c| c == '-').expect("No valid string");
    let (first, second) = string_value.split_at(pos);

    let first: u8 = first.parse().expect("Not a number");
    let second: u8 = second[1..].parse().expect("Not a number");

    first..=second
}

fn main() {
    println!("{:?}", parse_range("3-7").collect::<Vec<u8>>());
}

【讨论】:

  • 不会inclusive_range_syntax 修复第一个版本的"0-255" 中的溢出吗?
  • 您也可以通过返回std::ops::Range&lt;u8&gt; 来避免collect,并且不需要每晚。不幸的是,您仍然需要每晚返回RangeInclusive,但也许是这些日子之一......
  • 在夜间实现中,我宁愿从函数中返回 RangeInclusive 并将其转换为向量(或将其视为迭代器)给调用者。
  • @CodesInChaos 这就是我所做的。嗯...我没有返回RangeInclusive,但收集结果或用它做其他事情是调用者的决定。
【解决方案2】:

不是第一次调用collect,而是前进迭代器:

let mut values = string_value
    .splitn(2, "-")
    .map(|part| part.parse().unwrap());

let start = values.next().unwrap();
let end = values.next().unwrap();

不要调用.ok().unwrap()——这会将带有有用错误信息的Result 转换为没有任何信息的Option。只需直接在Result 上拨打unwrap

如前所述,如果您想返回Vec,您需要调用collect 来创建它。如果你想return an iterator,你可以。即使在稳定的 Rust 中也不错:

fn parse_range(string_value: &str) -> std::ops::Range<u8> {
    let mut values = string_value
        .splitn(2, "-")
        .map(|part| part.parse().unwrap());

    let start = values.next().unwrap();
    let end = values.next().unwrap();

    start..end + 1
}

fn main() {
    assert!(parse_range("1-5").eq(1..6));
}

很遗憾,inclusive ranges are not yet stable,所以你需要继续使用 +1 或切换到 nightly。


由于splitn(2, "-") 为任何有效的string_value 返回两个结果,因此最好将元组直接分配给first 和last 两个变量,而不是看似任意长度的Vec。我似乎无法用元组做到这一点。

这在 Rust 的类型系统中是不可能的。您要求dependent types,这是运行时值与类型系统交互的一种方式。您可能希望 splitn 返回 (&amp;str, &amp;str) 的值 2 和 (&amp;str, &amp;str, &amp;str) 的值 3。当参数是变量时,情况会变得更加复杂,尤其是在运行时设置时。

最接近的解决方法是运行时检查是否没有更多值:

assert!(values.next().is_none());

我觉得这样的支票不值钱。


另见:

【讨论】:

  • 我不喜欢使用 next() 两次的隐含性(仍然隐含必须恰好有两个值),但是 unwrap() 提示和返回迭代器都很棒!跨度>
  • @l0b0 我不确定您是否按照我理解的方式使用“隐式”一词。调用next 看起来很显式,有两个变量的事实也很显式
  • 我的意思是,我们没有明确说明我们已经使用了values 中的所有可用条目。如果我可以说(first, last)=,如果值是例如“5--6”或“5-6-”,那将是一个运行时错误。目前我不得不依赖不太明显的逻辑:parse() 不会简单地忽略前导或尾随破折号,而且我已经将正确的n 赋予了splitn - 我最初的实现使用了n=@987654358 @ 因为我直觉 nsplits 的数量,而不是返回项目的数量。如果出于某种原因将 n 设置为 3,则在处理无效输入时会出现未检测到的错误。
  • 因为我直觉 n 是拆分的数量,而不是返回项目的数量——那就是because of me,不客气!
  • @l0b0 我似乎记得有一些impl FromIterator&lt;T&gt; for Result&lt;(T, T, ...), NotEnoughItems&gt; 的建议允许这样的事情。不过,我现在找不到了。我认为它被阻止等待 const 泛型或其他一些未来功能......
猜你喜欢
  • 2011-03-28
  • 1970-01-01
  • 2016-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-22
相关资源
最近更新 更多