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

编程入门 行业动态 更新时间:2024-10-27 05:35:08
本文介绍了在Rust中处理多个`Option< T>`的惯用方式是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

由于我对Rust还是很陌生,所以我需要有关如何习惯地进行错误处理的指南.我发现错误处理样板真的很烦人.

Since I'm fairly new to Rust, I need guidance on how error handling is done idiomatically. I find the error-handling boilerplate really annoying.

我被多个Option<T>卡住了.太麻烦了,无法手动处理每个None案例.

I'm stuck with multiple Option<T>s. It's too verbose to handle each None case manually.

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

In Haskell, for example, you can chain optional value (Maybe) operations with a variety of operators: fmap, <*>, >>=, etc.:

f x = x * x g x = x ++ x main = print $ g <$> show <$> f <$> Just 2

在Rust中,这看起来是不可能的.我正在尝试将两个字符的卡字符串解析为结构Card:

The same looks impossible in Rust. I'm trying to parse a two-character card string into a struct Card:

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中看起来像这样:

This code would look like this in Haskell:

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使得(并且很容易)操纵monad的内部值成为可能.为了实现与之接近的功能,我必须编写chain函数,这似乎很容易理解:

Thanks to the chaining via >>= Haskell makes it possible (and easy!) to manipulate the inner value of a monad. In order to achieve something close to that I had to write the chain function, which seems strongly unidiomatic:

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) }

推荐答案

如上所述, Option 和 Result 上有吨实用程序方法.此外,try运算符(?)也可以用于返回失败或取消包装结果"的极为常见的情况.

As mentioned, Option and Result have tons of utility methods on them. Additionally, the try operator (?) can also be used for the extremely common case of "return the failure or unwrap the result"

我将为Face和Suit实现FromStr.您的代码将如下所示:

I'd implement FromStr for Face and Suit. Your code would then look like:

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 }) } }

如果没有,则可以使用Option上的现有方法.您没有定义Foo::from_usize,所以我假设要返回Foo,因此它将使用map:

If you didn't / couldn't, you can use the existing methods on Option. You didn't define Foo::from_usize, so I assume to returns Foo, so it would use map:

fn from_str(s: &str) -> Result<Self, Self::Err> { let mut c = s.chars(); let face = c .next() .and_then(|c| FACES.find(c)) .map(Face::from_usize) .ok_or(())?; let suit = c .next() .and_then(|c| SUITS.find(c)) .map(Suit::from_usize) .ok_or(())?; Ok(Card { face, suit }) }

  • Option::and_then
  • Option::map
  • Option::ok_or
    • Option::and_then
    • Option::map
    • Option::ok_or
    • 这两个路径均允许您出现有用错误,例如一个枚举,可让您知道衣服/面孔是否丢失/无效.错误类型()对使用者没有用.

      Both of these paths allow you to have useful errors, such as an enum that lets you know if the suit / face was missing / invalid. An error type of () is useless to consumers.

      您还可以定义Suit::from_char和Face::from_char,而不会泄漏数组的实现.

      You could also define Suit::from_char and Face::from_char and not leak the implementation of the array out.

      将它们放在一起:

      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 }) } }

      fn join<T>(x: Option<Option<T>>) -> Option<T>

      这是x.and_then(|y| y)

      fn bind<A, B, F>(x: Option<A>, f: F) -> Option<B> where F: FnOnce(A) -> Option<B>,

      这是x.and_then(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>,

      这是x.and_then(f).and_then(g)

      另请参阅:

      • 如何在Option上实现一些便捷的方法(例如flat_map,flatten)?

更多推荐

在Rust中处理多个`Option&lt; T&gt;`的惯用方式是什么?

本文发布于:2023-11-30 10:38:43,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1649680.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:多个   方式   Option   Rust   gt

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!