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

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

ただいまの
回答率

88.59%

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

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 621

hito4

score 10

前提・実現したいこと

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

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

該当のソースコード

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Default,Debug)]
struct X { x:usize, }
impl X {
    fn set_x_1(& mut self,x:usize) {
        self.x=x;
    }
//  fn set_x_2(&     self,x:usize) {
//      self.borrow_mut().x=x;
//  }
}

fn main() {
    let mut b = Box::new(X::default());
    let     r = Rc::new(RefCell::new(X::default()));

    println!("b1: {:?}",&b);
    println!("r1: {:?}",&r);

    b.set_x_1(100);
    r.borrow_mut().set_x_1(200);  //←こうではなく、
//  r.set_x_2(200);               //←こう書きたかった

    println!("b2: {:?}",&b);
    println!("r2: {:?}",&r);
}

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

上記コードからコメントアウトを消して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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

構造体が、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)になります。

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

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

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

(意訳)

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

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

// 実装したいメソッド(set_x_2())を含むトレイトを定義する
trait SetX {
    fn set_x_2(&self, x: usize);
}

// そのトレイトを対象の型(RefCell<X>)に実装する
impl SetX for RefCell<X> {
    fn set_x_2(&self, x: usize) {
        self.borrow_mut().x = x;
    }
}

let r = Rc::new(RefCell::new(X::default()));
r.set_x_2(200);

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

// 対象の型をラップする型を定義する
#[derive(Default, Debug)]
struct RefCellX(RefCell<X>);

// そのラッパー型に対してメソッドを実装する
impl RefCellX {
    fn set_x_2(&self, x: usize) {
        self.0.borrow_mut().x = x;
    }
}

let r = Rc::new(RefCellX::default());
r.set_x_2(200);

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/03/21 21:22

    早速1つ目の方法で動作を確認できました。ありがとうございます。

    1つ目の方が、正攻法のように思えました。
    2つ目は、まさかここで、1要素のタプル構造体を見ることになるとは…
    トリックというか、アートというのか… (^_^; 。

    入り組んだデータ構造をRustで使うためには、コンパイラの厳密な型の扱いに対応するため、トレイトや型ラッピングのテクニックが重要なのですね。
    大変助かりました。

    キャンセル

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

  • ただいまの回答率 88.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る