61551


Rustの勉強中です。  
あるVector(Pool::oroginal_data)に全データを管理させ、その一部をFinder::ref_dataから参照するコードを書きたいです。  
orignal_dataから要素を検索する際に、毎回orignal_data全体を見るのではなく、条件に合う要素をいくつかのFinderに入れておいて検索を速くすることを目指しています。  
ライフタイムを指定することでできるかと思ったのですが、orignal_dataの要素を参照にするところでコンパイラに怒られ、解決できませんでした。

[code]

    struct Finder {
        ref_data: Vec<&String>,
    }
    
    struct Pool {
        original_data: Vec<String>,
        finder_vec: Vec<Finder>,
    }
    
[/code]

こういうシチュエーションで、Rustではどのようにコーディングすべきかご教示願えますでしょうか。


CC BY-SA 4.0
2019-12-18T06:27:26.823
61537
2019-12-19T23:48:40.413
37118
question
rust
あるVectorの要素を別のVectorの要素として参照したい


こんにちは。  
参照をデータ構造の中に持つのはライフタイムに慣れてないと中々難しいですね。特にアプリケーション全体の設計にも関わるので正直なところデータ構造だけを提示されても正しい解決策が出せるかは怪しいです。

一応、下記の通りライフタイムを指定すればコンパイルの通るコードは作れます。しかしこれをそのままseino2005さんのアプリケーションに適用して同様にコンパイルが通るかは分かりません。`Pool`
や `Finder` がどう使われているか次第です。

[code]

    struct Finder<'a> {
        ref_data: Vec<&'a String>,
    }
    
    struct Pool<'a> {
        original_data: Vec<String>,
        finder_vec: Vec<Finder<'a>>,
    }
    
    impl<'a> Pool<'a> {
        pub fn new() -> Self {
            Pool {
                original_data: Vec::new(),
                finder_vec: Vec::new(),
            }
        }
    
        pub fn push_data(&mut self, data: String) {
            self.original_data.push(data)
        }
    
        pub fn create_prefix_finder(&'a mut self, prefix: &str) {
            let Pool {
                original_data,
                finder_vec,
            } = self;
            let ref_data = original_data
                .iter()
                .filter(|s| s.starts_with(prefix))
                .collect::<Vec<_>>();
            let finder = Finder { ref_data };
    
            finder_vec.push(finder)
        }
    }
    
    fn main() {
        let mut pool = Pool::new();
    
        pool.push_data("aiueo".into());
        pool.push_data("aoeui".into());
    
        pool.create_prefix_finder("a");
    }
    
[/code]

このコードのポイントを解説しておくと

  1. 構造体のフィールドに参照を持たせるときは構造体にもライフタイムパラメータが必要(`Finder` 、 `Pool` 両構造体)
  2. ライフタイムは推論ではなくルールに従って機械的に決められているのでルールが意図に沿わない場合は自分で宣言する必要がある(`create_prefix_finder` メソッド)。ルールについては[こちら](https://doc.rust-jp.rs/rust-nomicon-ja/lifetime-elision.html)を参照下さい。

もしこれで解決するようであればこれを使ってみて下さい。

他には参照カウント(RC)を使って共有する手があります。参照のカウント数をデータに持つ必要があるので多少のオーバーヘッドは乗りますが、ライフタイムに煩わされずにデータの共有ができます。

[code]

    use std::rc::Rc;
    
    struct Finder {
        ref_data: Vec<Rc<String>>,
    }
    
    struct Pool {
        original_data: Vec<Rc<String>>,
        finder_vec: Vec<Finder>,
    }
    
    impl Pool {
        pub fn new() -> Self {
            Pool {
                original_data: Vec::new(),
                finder_vec: Vec::new(),
            }
        }
    
        pub fn push_data(&mut self, data: String) {
            self.original_data.push(Rc::new(data))
        }
    
        pub fn create_prefix_finder(&mut self, prefix: &str) {
            let Pool {
                original_data,
                finder_vec,
            } = self;
            let ref_data = original_data
                .iter()
                .filter(|s| s.starts_with(prefix))
                .cloned()
                .collect::<Vec<_>>();
            let finder = Finder { ref_data };
    
            finder_vec.push(finder)
        }
    }
    
    fn main() {
        let mut pool = Pool::new();
    
        pool.push_data("aiueo".into());
        pool.push_data("aoeui".into());
    
        pool.create_prefix_finder("a");
    }
    
[/code]

こちらはアプリケーションの全体像が分からなくても大抵の場合で動きます。詳しくは公式ドキュメントの[スマートポインタの部分](https://doc.rust-
jp.rs/book/second-edition/ch15-04-rc.html)などを参照して下さい。

恐らく上記いずれかの方法で動くんじゃないでしょうか。


CC BY-SA 4.0
2019-12-18T14:10:54.923
61551
2019-12-18T14:10:54.923
22816
61537
answer


  * keenさんに教えて頂いたライフタイムの方法で解決できるかと思ったのですが、理解に苦しむコンパイルエラーとなり、断念しました。
  * 最終的にはもうひとつの方法であるRCで解決できました。

以下がライフタイムで記述した場合のコンパイルエラーです。  
`create_prefix_finder`を呼び出した後、poolのメソッドが呼び出せなくなりました。  
エラーメッセージからすると`create_prefix_finder`でborrowされたので他にはborrowできないとのこと。  
`fn push_data(&mut self, data: String)`も`fn dummy(&'a self)`でも同様のエラーでした。

`create_prefix_finder`のselfにライフタイム指定した影響だと思いますが、それでpool自身が使用できなくなるのは、私の今の知識ではわかりませんでした。  
元の質問はRcで解決として、この件は別途考えようと思います。

[code]

    struct Finder<'a> {
        ref_data: Vec<&'a String>,
    }
    
    struct Pool<'a> {
        original_data: Vec<String>,
        finder_vec: Vec<Finder<'a>>,
    }
    
    impl<'a> Pool<'a> {
        pub fn new() -> Self {
            Pool {
                original_data: Vec::new(),
                finder_vec: Vec::new(),
            }
        }
    
        pub fn push_data(&mut self, data: String) {
            self.original_data.push(data)
        }
    
        pub fn create_prefix_finder(&'a mut self, prefix: &str) {
            let Pool {
                original_data,
                finder_vec,
            } = self;
            let ref_data = original_data
                .iter()
                .filter(|s| s.starts_with(prefix))
                .collect::<Vec<_>>();
            let finder = Finder { ref_data };
    
            finder_vec.push(finder)
        }
    
        pub fn dummy(&'a self)
        {}
    
    }
    
    fn main() {
        let mut pool = Pool::new();
    
        pool.push_data("aiueo".into());
        pool.push_data("aoeui".into());
    
        pool.create_prefix_finder("a"); // mutable borrow occurs here
    
    //  pool.push_data("zzzzzz".into());
        pool.dummy();                  //  immutable borrow occurs here
                                       //  mutable borrow later used here
    
    
    }
    
    
[/code]


CC BY-SA 4.0
2019-12-19T23:48:40.413
61601
2019-12-19T23:48:40.413
37118
61537
answer