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

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

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

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

Q&A

解決済

1回答

226閲覧

Rust error[E0275]: overflow evaluating the requirement が解決できない

tt-44

総合スコア1

Rust

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

0グッド

0クリップ

投稿2024/11/21 22:27

実現したいこと

パーサコンビネータを実装しています。
このパーサコンビネータは、パースしたい対象にParserトレイトとその派生トレイトを実装することで汎用パーサを利用できるようにする形式です。例えば特定のパーサを5回繰り返すことを意味するrepeat関数は引数がrepeat(parser: impl Parser)->Repeatになっています。ここで、例えば&strはParserを実装しているのでrepeat("hello")とすることができます。また、RepeatもParserを実装しているので、repeatの結果もrepeatに渡すことができます。
Parserはパースする入力型、パーサが返す出力型、エラーの型と入力型に対するラッパ型であるInputを型引数で指定できます。例えば、「文字列をJsonという構造体にパースしたい。失敗したらJsonParseErrとしたい。入力型に追加情報はいらない」場合は、「Parse<&str, Json, JsonParseErr, SimpleInput<&str, JsonParseErr>>>」となります。Inputについては入力型のラッパ型です。例えば、「現在パースしている位置」を追加情報にしたい場合などに役立ちます。
Parserのスーパートレイトとして、TeminateParserトレイトがあります。これは、文脈自由文法でいう終端記号に対してパースを行います。ただしただの終端記号ではなく、Iterator的な終端記号のみ実装することができます。これが必要なのは、「x文字目でパースが失敗した」ということを表せるようにするためです。これを作ることで、「x文字目」という概念がない終端記号とを分類することができます。

発生している問題・分からないこと

提供しているコードで、タイトルのエラーが発生します。
一応このエラーについては調べて大枠は理解していますが、エラーの原因と回避策が思いつきませんでした。
特に私が理解できなかったのは、コード例のmain()内のケース1についてはエラーが出ますが、ケース2についてはエラーが出ないことです。
このエラーはケース2が実際にはエラーでもその前の行が同様にこのエラーだと、ケース2の診断を隠す場合があるので、ケース1をコメントアウトしてみましたが、それでもエラーは出ず、コンパイルが通りました。

エラーメッセージ

error

1error[E0275]: overflow evaluating the requirement `SimpleInput<String, ()>: Input<String, ()>` 2 --> src/main.rs:6:5 3 | 46 | <P as Parser<String, (), (), SimpleInput<String, ()>>>::parse(&mut P, &new::<String, (), SimpleInput<String, ()>>(format!(""))); 5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | 7note: required by a bound in `Parser::parse` 8 --> src/main.rs:12:61 9 | 1012 | trait Parser<I: Parser<I, I, E, Input>, O, E: Clone, Input: crate::Input<I, E>>: Clone { 11 | ^^^^^^^^^^^^^^^^^^ required by this bound in `Parser::parse` 1213 | fn parse(&mut self, input: &Input); 13 | ----- required by a bound in this associated function

該当のソースコード

rust

1fn main() { 2 // ケース1 3 <P as Parser<String, (), (), SimpleInput<String, ()>>>::parse(&mut P, &new::<String, (), SimpleInput<String, ()>>(format!(""))); 4 // ケース2 5 <P as Parser<String, (), WrapperErr<()>, WrapperInput<_, _>>>::parse(&mut P, &new(format!(""))); 6} 7fn new<I: Parser<I, I, E, Input>, E: Clone, Input: crate::Input<I, E>>(input: I) -> Input { 8 Input::new(input) 9} 10trait Parser<I: Parser<I, I, E, Input>, O, E: Clone, Input: crate::Input<I, E>>: Clone { 11 fn parse(&mut self, input: &Input)->Result<(Input, O),()>; 12} 13 14trait Input<I: Parser<I, I, E, Self>, E: Clone>: Sized { 15 fn new(input: I) -> Self; 16 fn input(&self)->&I; 17 fn errs(&self)->&Vec<E>; 18} 19trait TerminateParser<E: Clone>: Parser<Self, Self, WrapperErr<E>, WrapperInput<Self, E>> {} 20 21struct SimpleInput<I: Parser<I, I, E, Self>, E: Clone> { 22 input: I, 23 errs: Vec<E>, 24} 25impl<I: Parser<I, I, E, Self>, E: Clone> Input<I, E> for SimpleInput<I, E> { 26 fn new(input: I) -> Self { 27 SimpleInput { 28 input, 29 errs: vec![], 30 } 31 } 32 fn input(&self)->&I { 33 &self.input 34 } 35 fn errs(&self)->&Vec<E> { 36 &self.errs 37 } 38} 39 40#[derive(Clone)] 41struct WrapperErr<E: Clone>(E); 42#[derive(Clone)] 43struct WrapperInput<I: TerminateParser<E>, E: Clone> { 44 source: I, 45 index: usize, 46 errs: Vec<WrapperErr<E>>, 47} 48impl<I: TerminateParser<E>, E: Clone> Input<I, WrapperErr<E>> for WrapperInput<I, E> { 49 fn new(input: I) -> Self { 50 WrapperInput { 51 source: input, 52 index: 0, 53 errs: vec![], 54 } 55 } 56 fn input(&self)->&I { 57 todo!() //提示していないコードに依存した処理 58 } 59 fn errs(&self)->&Vec<WrapperErr<E>> { 60 &self.errs 61 } 62} 63 64//終端記号パーサの実装 65impl<E: Clone, Input: crate::Input<String, E>> Parser<String, String, E, Input> for String { 66 fn parse(&mut self, _input: &Input) -> Result<(Input, String), ()> { 67 todo!() 68 } 69} 70impl<E: Clone> TerminateParser<E> for String {} 71 72#[derive(Clone)] 73struct P; 74impl<I: Parser<I, I, E, Input>, O, E: Clone, Input: crate::Input<I, E>> Parser<I, O, E, Input> 75 for P 76{ 77 fn parse(&mut self, _input: &Input) -> Result<(Input, O), ()> { 78 todo!() 79 } 80}

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

Rustのエラー情報でエラーの理解を試みました。
しかし、私のコードにこのコード例を落とし込む方法と、このエラーの回避策はわかりませんでした。

補足

環境

>rustup --version rustup 1.27.1 (54dd3d00f 2024-04-24) info: This is the version for the rustup toolchain manager, not the rustc compiler. info: The currently active `rustc` version is `rustc 1.82.0 (f6e511eec 2024-10-15)`

できれば回避したいこと

このエラーを解決するうえで、できれば回避したい設計の改変

  • Parserのエラーのラッパ型を一律にすること。つまり、現状のSimpleInputのラッパ型がないEも、WrapperErrのエラー位置付きエラーもすべて一緒くたにErrorラッパとし、SimpleInputなどを使う場面でもOpiton<usize>でエラー位置情報を保持しなくてはならないタイプの設計。
  • Parse::parseの引数をInputトレイトではなくすこと。「位置付きエラーを利用できない」とはそもそもWrapperInputの{source, index}で実装できないということでまた「位置付きエラーを実現する」ためには、SimpleInputでは実現できない。なので、Inputトレイトを用いた今の実装が一番表現力が高い実装と考えている。

これらを避けることができない場合は、エラー解決のほうを優先します。

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

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

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

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

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

bsdfan

2024/11/22 12:37

Parser と Input で、トレイト境界が循環しているのが良くないと思います。 Input の I: Parser<I, I, E, Self> の境界については、今回の質問にあるコードだけみると不要に思います。
tt-44

2024/11/24 07:19 編集

コメントありがとうございます。返信遅れてすみません。 ご教示の通り、InputとSimpleInputのIの境界を削除したところ問題が解決しました! 自分は今まで型の境界を「想定していない型引数を拒否するもの」と考えていましたが、「実装に最低限必要な型引数の要件」を示すものに限定したほうがいい場合もあるのですね ベストアンサーに選びたいので同じ内容を回答欄に投稿いただけますでしょうか?
guest

回答1

0

ベストアンサー

Parser と Input で、トレイト境界が循環しているのが良くないと思います。
Input の I: Parser<I, I, E, Self> の境界については、今回の質問にあるコードだけみると不要に思います。

rust

1trait Input<I, E: Clone>: Sized { 2 // snip 3} 4 5struct SimpleInput<I, E: Clone> { 6 // snip 7} 8 9impl<I, E: Clone> Input<I, E> for SimpleInput<I, E> { 10 // snip 11} 12

自分は今まで型の境界を「想定していない型引数を拒否するもの」と考えていましたが、「実装に最低限必要な型引数の要件」を示すものに限定したほうがいい場合もあるのですね

「実装に最低限必要な型引数の要件」を示すものに限定したほうがいいというより、限定すべきに近いと思います。

投稿2024/11/24 11:06

bsdfan

総合スコア4818

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.34%

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

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

質問する

関連した質問