Rust 什么';s处理多个选项的惯用方法<;T>;`锈迹斑斑?
因为我对Rust还比较陌生,所以我需要关于如何按照惯用方式进行错误处理的指导。我发现错误处理样板文件真的很烦人 我被多个Rust 什么';s处理多个选项的惯用方法<;T>;`锈迹斑斑?,rust,monads,option,maybe,Rust,Monads,Option,Maybe,因为我对Rust还比较陌生,所以我需要关于如何按照惯用方式进行错误处理的指导。我发现错误处理样板文件真的很烦人 我被多个选项困住了。它太冗长,无法手动处理每个None案例 例如,在Haskell中,您可以将可选值(可能)操作与各种运算符链接起来:fmap,,=,等等: fx=x*x gx=x++x main=print$g仅显示f 2 同样的情况在锈迹中看起来是不可能的。我正在尝试将一个两个字符的卡片字符串解析为一个structcard: const FACES: &'static s
选项困住了。它太冗长,无法手动处理每个None
案例
例如,在Haskell中,您可以将可选值(可能
)操作与各种运算符链接起来:fmap
,
,=
,等等:
fx=x*x
gx=x++x
main=print$g仅显示f 2
同样的情况在锈迹中看起来是不可能的。我正在尝试将一个两个字符的卡片字符串解析为一个structcard
:
const FACES: &'static str = "23456789TJQKA";
const SUITS: &'static str = "CDHS";
enum Face { /* ... */ }
enum Suit { C, D, H, S }
struct Card {
face: Face,
suit: Suit
}
impl FromStr for Card {
type Err = ();
fn from_str(x: &str) -> Result<Self, Self::Err> {
let mut xs = x.chars();
let a = chain(xs.next(), |x| FACES.find(x), Face::from_usize);
let b = chain(xs.next(), |x| SUITS.find(x), Suit::from_usize);
if let (Some(face), Some(suit)) = (a, b) {
Ok(Card::new(face, suit))
} else {
Err(())
}
}
}
多亏了通过>=
Haskell的链接,使得操纵单子的内部值成为可能(而且很容易!)。为了实现类似的功能,我必须编写chain
函数,这似乎非常不符合逻辑:
fn join<T>(x: Option<Option<T>>) -> Option<T> {
if let Some(y) = x {
y
} else {
None
}
}
fn bind<A, B, F>(x: Option<A>, f: F) -> Option<B>
where
F: FnOnce(A) -> Option<B>,
{
join(x.map(f))
}
fn chain<A, B, C, F, G>(x: Option<A>, f: F, g: G) -> Option<C>
where
F: FnOnce(A) -> Option<B>,
G: FnOnce(B) -> Option<C>,
{
bind(bind(x, f), g)
}
fn连接(x:Option)->Option{
如果让一些(y)=x{
Y
}否则{
没有一个
}
}
fn绑定(x:Option,f:f)->Option
哪里
F:fNoce(A)->选项,
{
加入(x.map(f))
}
fn链(x:Option,f:f,g:g)->Option
哪里
F:fNoce(A)->选项,
G:FnOnce(B)->选项,
{
绑定(绑定(x,f),g)
}
您似乎想要:
pub fn和_then(self,f:f)->选项
哪里
F:FnOnce(T)->选项
示例:
fnsq(x:u32)->选项{Some(x*x)}
fn nope(u32)->选项{None}
断言!(一些(2)和(sq)和(sq),一些(16));
断言!(一些(2)和(sq)和(不),没有);
断言!(一些(2)。然后(不)。然后(sq),没有);
断言!(无。然后(sq)。然后(sq),无);
可能
-Rust的结果中的一元链结
是通过以下方式完成的。应该看起来像
fn from_str(x: &str) -> Result<Self, Self::Err> {
let mut xs = x.chars();
let a = try!(chain(xs.next(), |x| FACES.find(x), Face::from_usize));
let b = try!(chain(xs.next(), |x| SUITS.find(x), Suit::from_usize));
Ok(Card::new(face, suit))
}
fn from_str(x:&str)->结果{
设mut xs=x.chars();
让a=try!(chain(xs.next(),|x | FACES.find(x),Face::from_usize));
让b=try!(chain(xs.next(),|x | SUITS.find(x),Suit::from_usize));
Ok(卡片:新的(脸、套装))
}
如前所述,上面有大量实用方法。此外,try操作符(?
)也可用于极为常见的“返回故障或打开结果”的情况
我会为Face
和Suit
实现FromStr
。您的代码将如下所示:
impl FromStr for Card {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let face = s[0..1].parse()?;
let suit = s[1..2].parse()?;
Ok(Card { face, suit })
}
}
这两种路径都允许您出现有用的错误,例如一个枚举,可以让您知道西装/面是否丢失/无效。()
的错误类型对使用者没有用处
您还可以定义Suit::from_char
和Face::from_char
而不泄漏数组的实现
总而言之:
impl Suit {
fn from_char(c: char) -> Option<Self> {
use Suit::*;
[('c', C), ('d', D), ('h', H), ('s', S)]
.iter()
.cloned()
.find(|&(cc, _)| cc == c)
.map(|(_, s)| s)
}
}
enum Error {
MissingFace,
MissingSuit,
InvalidFace,
InvalidSuit,
}
impl FromStr for Card {
type Err = Error;
fn from_str(x: &str) -> Result<Self, Self::Err> {
use Error::*;
let mut xs = x.chars();
let face = xs.next().ok_or(MissingFace)?;
let face = Face::from_char(face).ok_or(InvalidFace)?;
let suit = xs.next().ok_or(MissingSuit)?;
let suit = Suit::from_char(suit).ok_or(InvalidSuit)?;
Ok(Card { face, suit })
}
}
这是x,然后是(f)
fn链
除了其他答案,您还可以查看一元表达式板条箱,如或。例如,对于
,使用map\u:
fn from_str(x: &str) -> Result<Self, Self::Err> {
let mut xs = x.chars();
map_for!{
ax <- xs.next();
f <- FACES.find(ax);
a <- Face::from_usize(f);
bx <- xs.next();
s <- SUITS.find(bx);
b <- Suit::from_usize (s);
=> Card::new(a, b) }
.ok_or(Err(()))
}
fn from_str(x:&str)->结果{
设mut xs=x.chars();
地图{
既然?
已经稳定下来,我想试试看!
就要退出了。让a=chain(xs.next(),|x | FACES.find(x),Face::from_usize)?;
看起来好一点。有意思。这基本上是旧版的语法糖吗?
试试看!
?@leftaround实际上它更好,因为它是由驱动的,所以它更灵活。例如,它适用于选项
,当它稳定后,它将可用于用户类型。
fn join<T>(x: Option<Option<T>>) -> Option<T>
fn bind<A, B, F>(x: Option<A>, f: F) -> Option<B>
where
F: FnOnce(A) -> Option<B>,
fn chain<A, B, C, F, G>(x: Option<A>, f: F, g: G) -> Option<C>
where
F: FnOnce(A) -> Option<B>,
G: FnOnce(B) -> Option<C>,
fn from_str(x: &str) -> Result<Self, Self::Err> {
let mut xs = x.chars();
map_for!{
ax <- xs.next();
f <- FACES.find(ax);
a <- Face::from_usize(f);
bx <- xs.next();
s <- SUITS.find(bx);
b <- Suit::from_usize (s);
=> Card::new(a, b) }
.ok_or(Err(()))
}