回答編集履歴
2
値をbox化するメソッドについて追記しました
answer
CHANGED
@@ -54,4 +54,29 @@
|
|
54
54
|
}
|
55
55
|
```
|
56
56
|
|
57
|
-
このように引数をとらないコンストラクタなら`Default`トレイトで実現できます。もしいくつかの共通の引数をとるようなコンストラクタが必要なら、`TraitB`とは別のトレイトを定義してみてください。
|
57
|
+
このように引数をとらないコンストラクタなら`Default`トレイトで実現できます。もしいくつかの共通の引数をとるようなコンストラクタが必要なら、`TraitB`とは別のトレイトを定義してみてください。
|
58
|
+
|
59
|
+
**追記**
|
60
|
+
|
61
|
+
質問からは逸れますが `Box::new(StructB::default())`のような値をbox化するコードを頻繁に書く場合は、`StructB`にbox化するメソッド(またはトレイトメソッド)を実装すると少し書きやすくなりそうです。([こちら](https://keens.github.io/blog/2019/12/23/jisakulintru_rutocargo_fixderustnoko_doworifakutasuru/index.html#%E5%AE%9F%E8%B7%B5lint-fix)で紹介されているアイデアです)
|
62
|
+
|
63
|
+
```rust
|
64
|
+
impl StructB {
|
65
|
+
fn boxed(self) -> Box<Self> {
|
66
|
+
Box::new(self)
|
67
|
+
}
|
68
|
+
|
69
|
+
fn boxed_new() -> Box<Self> {
|
70
|
+
Self::default().boxed()
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
fn main() {
|
75
|
+
let mut y: Box<dyn TraitB>;
|
76
|
+
// y = Box::new(StructB::default()) の代わりに以下のように書ける
|
77
|
+
y = StructB::default().boxed();
|
78
|
+
y = StructB::boxed_new();
|
79
|
+
}
|
80
|
+
```
|
81
|
+
|
82
|
+
この例では`boxed(self)`と`boxed_new()`を何かのトレイトのメソッドにする理由は特になかったので、`Struct`のメソッドや関連関数として実装しました。もしトレイトのメソッドにする必要があれば、(`TraitB`とは別にトレイトを定義すれば)そうすることも可能です。
|
1
わかりやすさのために文章を修正しました(特に冒頭の部分)
answer
CHANGED
@@ -1,14 +1,18 @@
|
|
1
|
-
>
|
1
|
+
> 下記コードのStructAがOKで、StructBがNGである理由がなかなか理解できず…。
|
2
2
|
|
3
|
-
|
3
|
+
こういうときは、まずエラーの解説を読んでみるのがおすすめです。
|
4
4
|
|
5
|
-
E0038
|
5
|
+
> error[E0038]: the trait `TraitB` cannot be made into an object
|
6
6
|
|
7
|
-
|
8
|
-
- トレイトメソッドがメソッドレシーバ(`&self`, `&muf self`, `self`)を持たない
|
7
|
+
コンパイラが出力する多くのエラーにはE0038のような番号がついています。この番号を使って、エラーの詳細な解説を表示できます。Webブラウザで [https://doc.rust-lang.org/error-index.html#E0038](https://doc.rust-lang.org/error-index.html#E0038) を開くか、ターミナルから `rustc --explain 38` を実行します。英語による説明ですが、最近は機械翻訳の性能向上が著しいので、それらを活用すればなんとかなりそうです。(例:[https://www.bing.com/translator?from=&to=ja](https://www.bing.com/translator?from=&to=ja))
|
9
8
|
|
10
|
-
|
9
|
+
E0038の解説では、どういうトレイトがトレイトオブジェクトにできないのか、いくつかの条件が書かれています。今回の`TraitB`が当てはまるのは以下の条件です。
|
11
10
|
|
11
|
+
- `Self`型の引数をとったり、`Self`型の戻り値を返したりするトレイトメソッドがある。(メソッドレシーバを示す`&self`などは問題ない)
|
12
|
+
- メソッドレシーバ(`&self`, `&muf self`, `self`)を持たないトレイトメソッドがある
|
13
|
+
|
14
|
+
これらがなぜダメなのかの理由を説明しだすと長くなるので、ここでは省略します。エラーの解説を機械翻訳にかけてみてください。また、いくつかの項目については、[ここ](https://doc.rust-jp.rs/book/second-edition/ch17-02-trait-objects.html?highlight=trait,obje#a%E3%83%88%E3%83%AC%E3%82%A4%E3%83%88%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AB%E3%81%AF%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E5%AE%89%E5%85%A8%E6%80%A7%E3%81%8C%E5%BF%85%E8%A6%81) で和訳されています。
|
15
|
+
|
12
16
|
> 定番の書き方があれば教えて頂けないでしょうか。
|
13
17
|
|
14
18
|
定番かどうかはわかりませんが、`TraitB`にコンストラクタにあたるトレイトメソッドを定義するのではなく、別のトレイトを使用する方法があります。ご質問のコードの`new()`に該当するトレイトメソッドは、標準ライブラリの`Default`トレイトですでに定義されていますので、それを使うといいでしょう。`Default`トレイトの定義は以下の通りです。
|
@@ -48,4 +52,6 @@
|
|
48
52
|
// default()メソッドを使う
|
49
53
|
let y: Box<dyn TraitB> = Box::new( StructB::default() );
|
50
54
|
}
|
51
|
-
```
|
55
|
+
```
|
56
|
+
|
57
|
+
このように引数をとらないコンストラクタなら`Default`トレイトで実現できます。もしいくつかの共通の引数をとるようなコンストラクタが必要なら、`TraitB`とは別のトレイトを定義してみてください。
|