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

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

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

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

Q&A

解決済

2回答

1910閲覧

構造体に別の構造体A又はBのポインタを持たせたい

hito4

総合スコア10

Rust

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

1グッド

1クリップ

投稿2020/02/29 17:05

前提・実現したいこと

私はこれまでC言語とPerlを使って趣味でプログラムを作ってきました。Rustは初心者です。

構造体のフィールドに、別の構造体A又はBのポインタ(Box)を持たせたいと思い
コードを自作してみたのですが、コンパイルが通りませんでした。
エラーメッセージから、型注釈が必要、matchのアームが同じ型になっていない、というあたりを
汲み取ってネットで検索したり、書籍を頼ったりしたのですが、解決できません。
型を抽象化しないといけないのではと思いimpl Traitを調べたり、
トレイトオブジェクトを調べたり、クロージャを調べたり…したのですが、
コードがどんどん複雑になってエラーも増える一方で、手に負えませんでした。

Rustにおける定番の書き方のようなものがあれば、教えて頂けないでしょうか。

該当のソースコード

Rust

1fn main() { 2 let x = StructX::new( TypeA ); 3 let y = StructX::new( TypeB ); 4} 5 6enum EnumX { 7 TypeA, 8 TypeB, 9} 10use self::EnumX::*; 11 12struct StructA {} //StructXのフィールドに入れたい構造体A 13struct StructB {} //StructXのフィールドに入れたい構造体B 14 15struct StructX<T> { 16 box_pt: Box<T>, // Boxで StructA 又は StructB をくるみたい 17} 18 19impl<T> StructX<T> { 20 fn new( t: EnumX ) -> Self { 21 let pt = match t { 22 TypeA => Box::new( StructA {} ), 23 TypeB => Box::new( StructB {} ), 24 }; 25 StructX { 26 box_pt: pt, 27 } 28 } 29}

発生している問題・エラーメッセージ

error[E0282]: type annotations needed for `StructX<T>` --> src/main.rs:2:13 | 2 | let x = StructX::new( TypeA ); | - ^^^^^^^^^^^^ cannot infer type for type parameter `T` | | | consider giving `x` the explicit type `StructX<T>`, where the type parameter `T` is specified error[E0308]: match arms have incompatible types --> src/main.rs:23:22 | 21 | let pt = match t { | __________________- 22 | | TypeA => Box::new( StructA {} ), | | ---------------------- this is found to be of type `std::boxed::Box<StructA>` 23 | | TypeB => Box::new( StructB {} ), | | ^^^^^^^^^^^^^^^^^^^^^^ expected struct `StructA`, found struct `StructB` 24 | | }; | |_________- `match` arms have incompatible types | = note: expected type `std::boxed::Box<StructA>` found struct `std::boxed::Box<StructB>` error: aborting due to 2 previous errors Some errors have detailed explanations: E0282, E0308. For more information about an error, try `rustc --explain E0282`. error: could not compile `tmp2`. To learn more, run the command again with --verbose.

補足情報(FW/ツールのバージョンなど)

OS: Windows10 + WSL1 + Ubuntu
Rust:1.41.0

denjiry👍を押しています

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

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

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

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

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

guest

回答2

0

ベストアンサー

元のコードがコンパイルエラーになる理由ですが、impl<T> StructX<T>の型パラメータTがコンパイル時に決まらないといけないのに、new()関数にはmatch式があるためにTが実行時まで決まらないためです。(ジェネリクスの型パラメータはコンパイル時に決まらないといけません)

解決法は二つあります。

一つ目の解決法はStructAStructBという二つの型を定義するのではなく、enumで一つの型として定義することです。

rust

1fn main() { 2 let x = StructX::new(TypeA); 3 let y = StructX::new(TypeB); 4} 5 6enum EnumX { 7 TypeA, 8 TypeB, 9} 10 11use self::EnumX::*; 12 13enum EnumAB { 14 A {}, // enumの個々のバリアントはstructのようにフィールドを持てる 15 B {}, 16} 17 18struct StructX { 19 ab: EnumAB, // Boxは特に持たなくてよい 20} 21 22impl StructX { 23 fn new(t: EnumX) -> Self { 24 let ab = match t { 25 TypeA => EnumAB::A {}, 26 TypeB => EnumAB::B {}, 27 }; 28 Self { ab } 29 } 30}

二つ目の解決法はトレイトを定義して、トレイトオブジェクト化することです。

rust

1// main()とEnumXの定義は上と同じなので省略 2 3trait TraitAB {} 4 5struct StructA {} 6struct StructB {} 7 8impl TraitAB for StructA {} 9impl TraitAB for StructB {} 10 11struct StructX { 12 box_pt: Box<dyn TraitAB>, 13} 14 15impl StructX { 16 fn new(t: EnumX) -> Self { 17 // ptに型アノテーションを付けることで、Box<StructA>とBox<StructB>を 18 // トレイトオブジェクトBox<dyn TraitAB>へ型強制(type coercion)する 19 let pt: Box<dyn TraitAB> = match t { 20 TypeA => Box::new(StructA {}), 21 TypeB => Box::new(StructB {}), 22 }; 23 StructX { box_pt: pt } 24 } 25}

もしこのプログラムをライブラリ化して、そのライブラリを使う側でTraitABを実装する型を自由に追加できるようにしたいなら、二つ目の方法を使います。そうでないなら、どちらの方法を使っても構わないです。

投稿2020/02/29 18:48

tatsuya6502

総合スコア2035

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

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

hito4

2020/03/01 02:04

Rustのenumは、機能がリッチで驚きました。データの構造をコード化するとき、struct一辺倒ではだめなのですね。 enumとトレイトオブジェクトとの使い分けも教えて頂き、ありがとうございます。自分のコードにどちらが合っているか、参考にさせていただきます。 ご教授ありがとうございました。
guest

0

Boxに入れる型を抽象化するには、入れたいstructに共通のトレイトを実装した上でBox<dyn Trait>を使うとよいです。

fn main() { let x = StructX::new( TypeA ); let y = StructX::new( TypeB ); } enum EnumX { TypeA, TypeB, } use self::EnumX::*; struct StructA {} //StructXのフィールドに入れたい構造体A struct StructB {} //StructXのフィールドに入れたい構造体B /* ここより下、変更箇所 */ trait GenericStruct{} // 共通のトレイト impl GenericStruct for StructA {} // それぞれについてトレイトを実装 impl GenericStruct for StructB {} // それぞれについてトレイトを実装 struct StructX { box_pt: Box<dyn GenericStruct>, // Boxに格納する型にdyn GenericStructを指定 } impl StructX { fn new( t: EnumX ) -> Self {      // 変数に型注釈をつけないとmatchで型エラーが出る let pt: Box<dyn GenericStruct> = match t { TypeA => Box::new( StructA {} ), TypeB => Box::new( StructB {} ), }; StructX { box_pt: pt, } } }

投稿2020/02/29 18:46

equal-l2

総合スコア172

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

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

hito4

2020/03/01 01:54

ご教授ありがとうございました。 トレイトオブジェクトを自分で調べた際は、確かオブジェクト安全性のエラーが出てしまい、先に進めなかったのです。今思うと、StructA/Bの方にコンストラクタを書いてしまい、そのコードにSelfが入っていたせいかもしれません…。いろいろ試して、記憶があやふやなのですが(汗)。 Box<dyn Trait>について勉強を進めてみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問