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

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

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

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

Q&A

解決済

1回答

1270閲覧

Box<T>からRc<RefCell<T>>への書き換えを少ない修正で済ませたい

hito4

総合スコア10

Rust

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

0グッド

1クリップ

投稿2020/03/21 08:20

前提・実現したいこと

Box<T>で書いたコードをRc<RefCell<T>>に書き直しています。
「.borrow_mut().」をメソッドの呼び出し側に追加すると修正箇所が増えるので、
メソッドの中に記述したいと考え、下記のようなコードを試しに作りました。

コメントアウトした箇所のように書きたかったのですが、
このままコメントを外して実行するとコンパイルエラーになります(E0599)。
エラーを回避できるメソッドの書き方はあるでしょうか。

該当のソースコード

Rust

1use std::rc::Rc; 2use std::cell::RefCell; 3 4#[derive(Default,Debug)] 5struct X { x:usize, } 6impl X { 7 fn set_x_1(& mut self,x:usize) { 8 self.x=x; 9 } 10// fn set_x_2(& self,x:usize) { 11// self.borrow_mut().x=x; 12// } 13} 14 15fn main() { 16 let mut b = Box::new(X::default()); 17 let r = Rc::new(RefCell::new(X::default())); 18 19 println!("b1: {:?}",&b); 20 println!("r1: {:?}",&r); 21 22 b.set_x_1(100); 23 r.borrow_mut().set_x_1(200); //←こうではなく、 24// r.set_x_2(200); //←こう書きたかった 25 26 println!("b2: {:?}",&b); 27 println!("r2: {:?}",&r); 28}

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

上記コードからコメントアウトを消してcargo runすると、コンパイルエラーが2つ発生します。
どちらも、メソッドがcurrent scopeで見つからないというエラーです。
helpにある「use std::borrow::BorrowMut;」を追加しても効果がないようでした。
構造体が、RcとRefCellによって二重にくるまれているので、そもそもimplの使い方が間違っているのでしょうか。

Compiling test_rcref v0.1.0 (/mnt/c/wsl_share/rust/test_rcref) error[E0599]: no method named `borrow_mut` found for reference `&X` in the current scope --> src/main.rs:11:14 | 11 | self.borrow_mut().x=x; | ^^^^^^^^^^ method not found in `&X` | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: | 1 | use std::borrow::BorrowMut; | error[E0599]: no method named `set_x_2` found for struct `std::rc::Rc<std::cell::RefCell<X>>` in the current scope --> src/main.rs:24:7 | 24 | r.set_x_2(200); //←こう書きたかった | ^^^^^^^ method not found in `std::rc::Rc<std::cell::RefCell<X>>` error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0599`. error: could not compile `test_rcref`. To learn more, run the command again with --verbose.

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

OS: Windows10 + WSL1 + Ubuntu
Rust:1.42.0

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

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

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

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

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

guest

回答1

0

ベストアンサー

構造体が、RcとRefCellによって二重にくるまれているので、そもそもimplの使い方が間違っているのでしょうか

そうですね。implの使い方に問題があります。set_x_2()メソッドはXに対してimplされていますので、メソッドレシーバ(第一引数)の&self&X型になります。その一方でborrow_mut()RefCell<X>のメソッドです。set_x_2()内でself.borrow_mut()とすると&Xborrow_mut()メソッドを呼び出すことになりますが、そういうメソッドは定義されてないためエラーになります。

つまりset_x_2()メソッドをXではなくRefCell<X>に対して実装する必要があります。

impl Xと書くのではなく、impl RefCell<X>と書けるといいのですが、これはエラー(E0116)になります。

console

1error[E0116]: cannot define inherent `impl` for a type outside of 2the crate where the type is defined

Rustでは他のクレートで定義された型に対してimplできない規則になっています。

E0116エラーの解説を見ると、このエラーの回避方法が2つ書かれています。

(意訳)

  • 実装したいメソッドを含むトレイトを定義し、そのトレイトを対象の型に実装する
  • 対象の型をラップする(包む)型を定義して、それに対してメソッドを実装する

1つ目の方法ならこうなります。

rust

1// 実装したいメソッド(set_x_2())を含むトレイトを定義する 2trait SetX { 3 fn set_x_2(&self, x: usize); 4} 5 6// そのトレイトを対象の型(RefCell<X>)に実装する 7impl SetX for RefCell<X> { 8 fn set_x_2(&self, x: usize) { 9 self.borrow_mut().x = x; 10 } 11} 12 13let r = Rc::new(RefCell::new(X::default())); 14r.set_x_2(200);

2つ目の方法ならこうなります。

rust

1// 対象の型をラップする型を定義する 2#[derive(Default, Debug)] 3struct RefCellX(RefCell<X>); 4 5// そのラッパー型に対してメソッドを実装する 6impl RefCellX { 7 fn set_x_2(&self, x: usize) { 8 self.0.borrow_mut().x = x; 9 } 10} 11 12let r = Rc::new(RefCellX::default()); 13r.set_x_2(200);

投稿2020/03/21 09:27

tatsuya6502

総合スコア2035

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

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

hito4

2020/03/21 12:22

早速1つ目の方法で動作を確認できました。ありがとうございます。 1つ目の方が、正攻法のように思えました。 2つ目は、まさかここで、1要素のタプル構造体を見ることになるとは… トリックというか、アートというのか… (^_^; 。 入り組んだデータ構造をRustで使うためには、コンパイラの厳密な型の扱いに対応するため、トレイトや型ラッピングのテクニックが重要なのですね。 大変助かりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問