首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在Rust中处理多个`Option<T>`的惯用方法是什么?

在Rust中处理多个`Option<T>`的惯用方法是什么?
EN

Stack Overflow用户
提问于 2018-06-07 08:34:25
回答 3查看 4.7K关注 0票数 10

因为我是Rust的新手,所以我需要关于如何以惯用的方式完成错误处理的指导。我发现错误处理样板真的很烦人。

我被多个Option<T>卡住了,手动处理每个None的情况太繁琐了。

例如,在Haskell中,您可以将可选值(Maybe)操作与各种运算符链接在一起:fmap<*>>>=等:

代码语言:javascript
复制
f x = x * x
g x = x ++ x
main = print $ g <$> show <$> f <$> Just 2

在Rust中,同样的情况看起来是不可能的。我正在尝试将一个两个字符的卡片字符串解析为一个结构Card

代码语言:javascript
复制
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中应该是这样的:

代码语言:javascript
复制
import Data.List (elemIndex)
x = Just 'C'
suits = "CDHS"
data Suit = C | D | H | S deriving Show
fromInt 0 = C
find = flip elemIndex
main = print $ x >>= find suits >>= return . fromInt

多亏了通过>>=的链接,Haskell让这一切成为可能(而且很容易!)操纵单子的内在价值。为了实现类似的功能,我不得不编写chain函数,它看起来很统一:

代码语言:javascript
复制
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)
}
EN

回答 3

Stack Overflow用户

发布于 2018-06-07 08:45:00

看起来你想要Option::and_then

代码语言:javascript
复制
pub fn and_then<U, F>(self, f: F) -> Option<U> 
where
    F: FnOnce(T) -> Option<U>

示例:

代码语言:javascript
复制
fn sq(x: u32) -> Option<u32> { Some(x * x) }
fn nope(_: u32) -> Option<u32> { None }

assert_eq!(Some(2).and_then(sq).and_then(sq), Some(16));
assert_eq!(Some(2).and_then(sq).and_then(nope), None);
assert_eq!(Some(2).and_then(nope).and_then(sq), None);
assert_eq!(None.and_then(sq).and_then(sq), None);
票数 8
EN

Stack Overflow用户

发布于 2018-06-07 15:07:03

除了其他答案之外,您还可以查看像mdomap_for这样的一元表达式箱。例如,使用map_for

代码语言:javascript
复制
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(()))
}

完全公开:我是map_for板条箱的作者。

票数 5
EN

Stack Overflow用户

发布于 2018-06-07 08:53:15

Rust的Result中的Maybe-monadic链接是由the try! macro完成的。应该看起来像这样

代码语言:javascript
复制
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))
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50731439

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档