我有以下代码。它有效。
但是我更感兴趣的是写make_tea
我称之为两个函数:get_milk_from_cow
和pour_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
。
答案 0 :(得分:2)
首先要做的事情是:上面的代码似乎没有任何问题,但我会假设您正在寻找更具惯用性的内容。
虽然需要比你的方法稍微多一点的内存,但连接字符串结果最优雅的方法是:
fn make_tea() -> Result<String, TeaError> {
vec![get_milk_from_cow(true), pour_milk(true)].into_iter()
.collect()
}
说明:
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())
}
必须创建向量,因为数组不提供自有元素的迭代器(&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)?)
}