質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

2回答

2498閲覧

RustのResult型の処理が分かりません。(rustlingsの問題)

KOTTON

総合スコア47

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2021/09/04 12:40

rustlingsのerrors6の問題が分かりません。
ParsePosNonzeroError Enumのメソッド追加とparse_pos_nonzero関数の編集を行って、下にあるtest_parse_errorというテストをパスするように改修するというのが全体の目的です。
不親切にもrustlingsは回答を一切用意していないのでここで質問させていただきます。
分かる方よろしくお願いします。

rust

1// errors6.rs 2 3// Using catch-all error types like `Box<dyn error::Error>` isn't recommended 4// for library code, where callers might want to make decisions based on the 5// error content, instead of printing it out or propagating it further. Here, 6// we define a custom error type to make it possible for callers to decide 7// what to do next when our function returns an error. 8 9// Make these tests pass! Execute `rustlings hint errors6` for hints :) 10 11// I AM NOT DONE 12 13use std::num::ParseIntError; 14 15// This is a custom error type that we will be using in `parse_pos_nonzero()`. 16#[derive(PartialEq, Debug)] 17enum ParsePosNonzeroError { 18 Creation(CreationError), 19 ParseInt(ParseIntError) 20} 21 22impl ParsePosNonzeroError { 23 fn from_creation(err: CreationError) -> ParsePosNonzeroError { 24 ParsePosNonzeroError::Creation(err) 25 } 26 // TODO: add another error conversion function here. 27} 28 29fn parse_pos_nonzero(s: &str) 30 -> Result<PositiveNonzeroInteger, ParsePosNonzeroError> 31{ 32 // TODO: change this to return an appropriate error instead of panicking 33 // when `parse()` returns an error. 34 let x: i64 = s.parse().unwrap(); 35 PositiveNonzeroInteger::new(x) 36 .map_err(ParsePosNonzeroError::from_creation) 37} 38 39// Don't change anything below this line. 40 41#[derive(PartialEq, Debug)] 42struct PositiveNonzeroInteger(u64); 43 44#[derive(PartialEq, Debug)] 45enum CreationError { 46 Negative, 47 Zero, 48} 49 50impl PositiveNonzeroInteger { 51 fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> { 52 match value { 53 x if x < 0 => Err(CreationError::Negative), 54 x if x == 0 => Err(CreationError::Zero), 55 x => Ok(PositiveNonzeroInteger(x as u64)) 56 } 57 } 58} 59 60#[cfg(test)] 61mod test { 62 use super::*; 63 64 #[test] 65 fn test_parse_error() { 66 // We can't construct a ParseIntError, so we have to pattern match. 67 assert!(matches!( 68 parse_pos_nonzero("not a number"), 69 Err(ParsePosNonzeroError::ParseInt(_)) 70 )); 71 } 72 73 #[test] 74 fn test_negative() { 75 assert_eq!( 76 parse_pos_nonzero("-555"), 77 Err(ParsePosNonzeroError::Creation(CreationError::Negative)) 78 ); 79 } 80 81 #[test] 82 fn test_zero() { 83 assert_eq!( 84 parse_pos_nonzero("0"), 85 Err(ParsePosNonzeroError::Creation(CreationError::Zero)) 86 ); 87 } 88 89 #[test] 90 fn test_positive() { 91 let x = PositiveNonzeroInteger::new(42); 92 assert!(x.is_ok()); 93 assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap())); 94 } 95}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tatsuya6502

2021/09/04 15:22

Teratialではコードを書いてほしい、課題を解いてほしいなどの質問は推奨されていません。 https://teratail.com/help/avoid-asking 実際に作業に取り組んで、つまづいたことについて質問してください。`rustlings hint errors6`でヒントを表示すると少し先に進めるかもしれません。
KOTTON

2021/09/04 23:53

もちろん自分でいろいろ試したんですが、質問した時点で最初の状態まで戻してしまっていたのでこのような形で質問してしまいました。すみませんでした。 今朝改めて試したところ無事クリアできました。
tatsuya6502

2021/09/05 01:05

そうだったんですね。承知しました。また、無事クリアできたということで良かったです! 参考までに、模範解答のようなものを回答欄に書きました。 rustlingsなどを進めるうえでのカジュアルな質問(解き方のヒントや、回答を添削してほしいなど)については、SlackのRust日本語コミュニティーで聞いてみるのもいいかもしれません。よかったら参加してみてください。 - https://rust-jp.slack.com/ - 入会フォーム: http://rust-jp.herokuapp.com/
guest

回答2

0

自力でクリアできたということで良かったです!

回答の以下のところですが、

rust

1let x = s.parse(); 2let x = match x { 3 Ok(n) => n, 4 Err(err) => { 5 return Err(ParsePosNonzeroError::from_parseint(err)); 6 } 7};

これが最も基本的な書き方なので、もちろんこれも正解の一つなわけですが、参考までに、もう少し簡潔に書ける方法を紹介します。

このmatch式ですが、以下のような形をしています。

rust

1match x { 2 // Okのときはunwrapする 3 Ok(n) => n, 4 // Errのときは、別のエラーに変換し、早期リターンする 5 Err(err) => return Err(別のエラー), 6}

このパターンはRustのエラー処理で頻出しますので、もっと簡単に書けるようにメソッドなどが提供されています。それらを使うと回答は以下のように書けます。

rust

1let x = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;

これについて詳しく見てみましょう。

map_err

まずmap_errに注目します。これは頻出パターンに出てきた「Errのときは別のエラーに変換する」ためのメソッドです。

rust

1map_err(ParsePosNonzeroError::from_parse_int)

Result型のmap_errメソッド(ドキュメント)は、クロージャーを引数にとり、そのクロージャーを使って、あるErr型から別のErr型へ変換します。定義は以下のようになっています。

rust

1pub fn map_err<F, O: FnOnce(E) -> F>(self, op: O) -> Result<T, F> { 2 match self { 3 Ok(t) => Ok(t), 4 Err(e) => Err(op(e)), 5 } 6}

クロージャーopの型Oの定義はFnOnce(E) -> Fで、これは、任意の型Eの引数を1つとり、任意の型Fの値を返すクロージャーを意味します。

さて、上の回答例ではmap_errにクロージャーではなく、関数名(ParsePosNonzeroError::from_parse_int)が渡されています。これは以下のようにクロージャーを使って書くのと同じ動きをします。

rust

1map_err(|e| ParsePosNonzeroError::from_parse_int(e))

Rustではクロージャーが求められているところに関数名やメソッド名を渡すことができます。ただし、その関数・メソッドは、求められているクロージャーの引数と関数の型の要件を満たしていなければなりません。

from_parse_intメソッドはParseIntError型の引数を1つとり、ParsePosNonzeroError型の値を返します。これはmap_erropクロージャーに求める「任意の型Eの引数を1つとり、任意の型Fの値を返す」という要件を満たしています。そういうときは、わざわざ|e| ParsePosNonzeroError::from_parse_int(e)のようにクロージャーを作らずに、ParsePosNonzeroError::from_parse_intを渡すことができます。

?演算子

次に回答例の行の右端にある?に注目しましょう。これは「Okのときはunwrapし、Errのときは早期リターンする」演算子です。

rust

1map_err(ParsePosNonzeroError::from_parse_int)?;

?はRust組み込みの演算子なのでRustコードによる定義はないのですが、以下のコードと大体同じことをします。

rust

1match x { 2 Ok(t) => t, 3 // From::fromはある型から別の型への変換が可能なら、それを行う 4 Err(e) => return Err(std::convert::From::from(e)), 5}

説明は以上になります。これらを組み合わせることで、以下のmatch式を

rust

1let x = s.parse(); 2let x = match x { 3 Ok(n) => n, 4 Err(err) => { 5 return Err(ParsePosNonzeroError::from_parseint(err)); 6 } 7};

以下のように書くことができるわけです。

rust

1let x = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;

参考になれば幸いです。

投稿2021/09/05 00:55

tatsuya6502

総合スコア2035

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KOTTON

2021/09/13 03:26

すぐ気が付かず、返信が遅れてしまい申し訳ありません! とてもとても丁寧な説明、ありがとうございます! 一番簡潔な書き方についてよく理解できました! Rustのコミュニティを探していたので、Slackにも参加させていただきます、ありがとうございます!
tatsuya6502

2021/09/13 11:06

> Rustのコミュニティを探していたので、Slackにも参加させていただきます はい。では、そちらでもお会いしましょう!
guest

0

自己解決

自己解決しました。
let x = match x {}
という表現を使い、さらにその中でResult型をreturnすることで解決できました。

rust

1fn parse_pos_nonzero(s: &str) 2 -> Result<PositiveNonzeroInteger, ParsePosNonzeroError> 3{ 4 // TODO: change this to return an appropriate error instead of panicking 5 // when `parse()` returns an error. 6 let x = s.parse(); 7 let x = match x { 8 Ok(n) => n, 9 Err(err) => { 10 return Err(ParsePosNonzeroError::from_parseint(err)); 11 } 12 }; 13 PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation) 14}

投稿2021/09/04 23:49

KOTTON

総合スコア47

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問