在Rust中编写错误:在Ok上结束结果

时间:2017-05-04 07:38:02

标签: error-handling rust

我有以下代码。它有效。

但是我更感兴趣的是写make_tea我称之为两个函数:get_milk_from_cowpour_milk。他们都返回Result<String, TeaError>

我如何撰写它们,以便我可以在String成功结束,如果它们成功,否则会返回错误。

enum TeaError {
    NoMilk,
    NoCup,
    NoCow,
    NoGas,
    NoSomething,
}

fn get_milk_from_cow(cow: bool) -> Result<String, TeaError> {
    if cow {
        Ok(String::from("get milk from cow"))
    } else {
        Err(TeaError::NoCow)
    }
}

fn pour_milk(milk: bool) -> Result<String, TeaError> {
    if milk {
        Ok(String::from("poured milk"))
    } else {
        Err(TeaError::NoMilk)
    }
}

fn make_tea() -> Result<String, TeaError> {
    let mut process = String::new();
    let step_cow = get_milk_from_cow(true)?;
    let step_milk = pour_milk(true)?;
    process.push_str(step_cow.as_str());
    process.push_str(step_milk.as_str());
    Ok(process)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn attemp_tea_one() {
        match pour_milk(false) {
            Err(v) => match v {
                TeaError::NoMilk => assert!(true),
                _ => assert!(false)
            },
            Ok(_) => assert!(false)
        };
    }

    #[test]
    fn attemp_tea_two() {
        match make_tea() {
            Err(_) => assert!(false),
            Ok(_) => assert!(true)
        };
    }
}

我试过了:

process.push_str(get_milk_from_cow(true)?
       .push_str(pour_milk(true)?.as_str()))

但它给出了:

error[E0308]: mismatched types
  --> src/errors.rs:27:22
   |
27 |     process.push_str(get_milk_from_cow(true)?.push_str(pour_milk(true)?.as_str()));
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &str, found ()
   |
   = note: expected type `&str`
              found type `()`

因为push_str不返回字符串。

修改

fn append(s1: String, s2: String) -> String {
    s1 + s2.as_str()
}
fn make_tea() -> Result<String, TeaError> {
    let process = append(get_milk_from_cow(true)?,
                         pour_milk(true)?);
    Ok(process)
}

所以,我可以做append(append(funcA(), funcB()), funcC())等等.. 我仍然在学习有关生命的时间,并考虑在内存分配中是否仍然可以改进append

2 个答案:

答案 0 :(得分:2)

首先要做的事情是:上面的代码似乎没有任何问题,但我会假设您正在寻找更具惯用性的内容。

虽然需要比你的方法稍微多一点的内存,但连接字符串结果最优雅的方法是:

fn make_tea() -> Result<String, TeaError> {
    vec![get_milk_from_cow(true), pour_milk(true)].into_iter()
        .collect()
}

Playground

说明:

  • 向量被消耗到Result<String, TeaError>(拥有,而不是引用)的迭代器中。
  • collect然后依赖于FromIterator的两个实现:
    • impl<A, E, V> FromIterator<Result<A, E>> for Result<V, E> where V: FromIterator<A>生成一个结果,其中包含从A的迭代器构建T的结果或从迭代器检索到的第一个错误。这就像使用收集的迭代器将结果的迭代器转换为结果一样。
    • impl FromIterator<String> for String将所有字​​符串连接成一个拥有的字符串

因此,只要您已经拥有一个将您的流程转换为一系列独立操作的迭代器,您就可以collect

现在,为了防止在发现错误后调用后续操作,它更容易坚持?运算符。

fn make_tea() -> Result<String, TeaError> {
    Ok(vec![get_milk_from_cow(true)?, pour_milk(true)?].into_iter()
        .collect())
}

Playground

必须创建向量,因为数组不提供自有元素的迭代器(&T而不是T)。但是,我们可以使用额外的映射来解决这个问题:

fn make_tea() -> Result<String, TeaError> {
    Ok([get_milk_from_cow(true)?, pour_milk(true)?].into_iter()
        .map(|a| a.as_str())
        .collect())
}

这会将&String中的元素映射到&str,这些元素也可以同样收集。

答案 1 :(得分:2)

此代码对已加星标的行进行了多余的工作:

fn make_tea() -> Result<String, TeaError> {
*   let mut process = String::new();
    let step_cow = get_milk_from_cow(true)?;
    let step_milk = pour_milk(true)?;
*   process.push_str(step_cow.as_str());
    process.push_str(step_milk.as_str());
    Ok(process)
}

process.push_str(step_cow.as_str())只会制作step_cow的不需要的副本。相反,尝试

fn make_tea() -> Result<String, TeaError> {
    let mut process = get_milk_from_cow(true)?;
    process.push_str(&pour_milk(true)?);
    Ok(process)
}

或者更方便的是

fn make_tea() -> Result<String, TeaError> {
    Ok(get_milk_from_cow(true)? + &pour_milk(true)?)
}