tagawa0525
@tagawa0525 (h tgw)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Rustの借用とライフタイムがさっぱりわからん

Q&A

解決したいこと

自身が保持するVectorへの参照を保持することができない。
集合NとSがあり、それらの集合から1つずつ選択した組み合わせを保持したいができない。

発生している問題・エラー

error[E0502]: cannot borrow `pairs.selected` as immutable because it is also borrowed as mutable
  --> src\main.rs:82:25
   |
69 |         pairs.make_pairs()
   |         ----- mutable borrow occurs here
...
82 |         for (idx, p) in pairs.selected.iter().enumerate() {
   |                         ^^^^^^^^^^^^^^
   |                         |
   |                         immutable borrow occurs here
   |                         mutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `prac_lifetime` (bin "prac_lifetime") due to 1 previous error

該当するソースコード

struct N {
    n: i32,
}

struct S {
    s: String,
}

struct Pair<'a> {
    n: &'a N,
    s: &'a S,
}

struct Pairs<'a> {
    selected: Vec<Pair<'a>>,
    ns: Vec<N>,
    ss: Vec<S>,
}

impl<'a> Pair<'a> {
    fn new(n: &'a N, s: &'a S) -> Pair<'a> {
        Pair { n, s }
    }
}

impl<'a> Pairs<'a> {
    fn new(ns: Vec<N>, ss: Vec<S>) -> Pairs<'a> {
        Pairs {
            selected: Vec::new(),
            ns,
            ss,
        }
    }

    fn min_len(&self) -> usize {
        let vec_len = vec![self.ns.len(), self.ss.len()];
        *vec_len.iter().min().unwrap()
    }

    fn make_pairs(&'a mut self) {
        for i in 0..self.min_len() {
            let pair = Pair::new(&self.ns[i], &self.ss[i]);
            println!("pair : ({}, {})", pair.n.n, pair.s.s);
            self.selected.push(pair);
        }
    }
}

fn main() {
    let ns = vec![
        N { n: 1 },
        N { n: 2 },
        N { n: 3 },
        N { n: 4 },
        N { n: 5 },
        N { n: 6 },
    ];

    let ss = vec![
        S { s: "a".to_string() },
        S { s: "b".to_string() },
        S { s: "c".to_string() },
        S { s: "d".to_string() },
    ];

    let mut pairs = Pairs::new(ns, ss);
    {
        // 一応スコープを分けたが無駄だった。
        pairs.make_pairs();
    }
    // 関数に切り出さなければコンパイルが通る
    // for i in 0..pairs.min_len() {
    //     let pair = Pair::new(&pairs.ns[i], &pairs.ss[i]);
    //     println!("pair : ({}, {})", pair.n.n, pair.s.s);
    //     pairs.selected.push(pair);
    // }

    // 以下をコメントアウトすればコンパイルが通る
    {
        // 一応スコープを分けたが無駄だった。
        // error[E0502]: cannot borrow `pairs.pairs` as immutable because it is also borrowed as mutable
        for (idx, p) in pairs.selected.iter().enumerate() {
            println!("pair[{}] : ({}, {})", idx, p.n.n, p.s.s);
        }
    }
}

自分で試したこと

  • 関数に切り出さなければ意図したとおりになる
  • 後続の表示部分をコメントアウトすればコンパイルできるが、以降使用できないのは困る
0

2Answer

Rustの流儀としては「オブジェクトはimmutableが基本」なのでselectedPairsのメンバになっているのが悪い、make_pairsはself.selectedを書き換えるのではなくVec<Pair<'a>>型を作成して返す(selfは変更しない)、とするのがRustのポリシーに沿った解決策のような気がします。
どうしてもselectedPairsのメンバとしたいのであれば、selectedの作成はPairsのコンストラクタ内で行うのが筋かと。

2Like

Comments

  1. @tagawa0525

    Questioner

    selfを変更しないということで、データの実体であるPool(変更しない)と参照のPairs(変更可能)に分けてみました。

    struct Pairs<'a> {
        selected: Vec<Pair<'a>>,
    }
    
    struct Pool {
        ns: Vec<N>,
        ss: Vec<S>,
    }
    
    impl<'a> Pairs<'a> {
        fn new() -> Pairs<'a> {
            Pairs {
                selected: Vec::new(),
            }
        }
    
        fn create(pool: &Pool) -> Pairs {
            let mut pairs = Pairs::new();
            pairs.add_paris(pool);
            pairs
        }
    
        fn add_paris(&mut self, pool: &'a Pool) {
            let ns_len = pool.ns.len();
            let ss_len = pool.ss.len();
            for idx in 0..pool.max_len() {
                let pair = Pair {
                    n: &pool.ns[idx % ns_len],
                    s: &pool.ss[idx % ss_len],
                };
                self.selected.push(pair);
            }
        }
    
        fn print(&self) {
            for (idx, p) in self.selected.iter().enumerate() {
                println!("pair[{}] : ({}, {})", idx, p.n.n, p.s.s);
            }
            println!();
        }
    }
    
    impl Pool {
        fn new(ns: Vec<N>, ss: Vec<S>) -> Pool {
            Pool { ns, ss }
        }
    
        fn max_len(&self) -> usize {
            *vec![self.ns.len(), self.ss.len()].iter().max().unwrap()
        }
    }
    
    fn main() {
        // mutをつけても、78行目以降はnsは変更できない。
        let mut ns = vec![
            N { n: 999 },
            N { n: 2 },
            N { n: 3 },
            N { n: 4 },
            N { n: 5 },
            N { n: 6 },
        ];
    
        let ss = vec![
            S { s: "a".to_string() },
            S { s: "b".to_string() },
            S { s: "c".to_string() },
            S { s: "d".to_string() },
        ];
    
        // 借用される前は変更可能
        ns[0].n = 1;
        ns.push(N { n: 7 });
    
        let pool = Pool::new(ns, ss);
    
        // // 以下をできなくするのが目的のためある程度納得。
        // ns[0].n = 0;
        // ns.push(N { n: 8 });
    
        let mut pairs = Pairs::create(&pool);
        pairs.print();
    
        // Vec<Pair<'a>>自体は変更できる。
        pairs.add_paris(&pool);
        pairs.print();
    }
    
    struct N {
        n: i32,
    }
    
    struct S {
        s: String,
    }
    
    struct Pair<'a> {
        n: &'a N,
        s: &'a S,
    }
    
  2. なるほどPairsで扱いたいのはselectedの方で、nsやssはただのデータソースであってPairs内部で保持する必要はないんですね。

    どうして変更後のコードだとエラーにならなかったのかわからなくて考え込んだのですが、

    • 元コードのmake_pairs()ではselectedにmutableなselfへの参照を格納しているので、以後はselectedはimmutableになれない
      iter()でエラー
    • 変更後のadd_paris()selectedにimmutableなpoolへの参照を格納しているので、以後もselectedはimmutableになれる
      iter()を使える
    • 変更後のコードでPool::new(ns, ss)を呼び出すとns,ssは外部オブジェクト(pool)からimmutableとして参照されている状態になるので、以後ns,ssはmutableになれなくなる
    • 元コードでmake_pairs()と同じ処理をmain()内に直接書けばiter()を使えたということは、上記の制約は別関数への貸し出しがあったときのみ発生する?

    ってことみたいですね。これはわかりにくいなぁ:sweat:
    でも予期しない変更を防ぐ効果があるのも理解できる。

  3. @tagawa0525

    Questioner

    【追記】追加のコメントと入れ違いになった模様ですが、本コメントは1つ目のコメントへの返信です。

    後半はコンストラクタ(Pairs::create_n())で頑張ってみても難しそうでした。
    おっしゃる通り、自身のメンバ変数を参照するのは悪手っぽいです。

    struct Pairs<'a> {
        selected: Vec<Pair<'a>>,
        ns: Vec<N>,
        ss: Vec<S>,
    }
    
    impl<'a> Pairs<'a> {
        fn new() -> Pairs<'a> {
            Pairs {
                selected: Vec::new(),
                ns: Vec::new(),
                ss: Vec::new(),
            }
        }
        fn create_1(ns: Vec<N>, ss: Vec<S>) -> Pairs<'a> {
            let mut obj = Pairs::new();
            obj.ns = ns;
            obj.ss = ss;
            // obj.add(); // error[E0505]: cannot move out of `obj` because it is borrowed
            obj
        }
    
        fn create_2(ns: Vec<N>, ss: Vec<S>) -> Pairs<'a> {
            let ns_len = ns.len();
            let ss_len = ss.len();
            let max_len = ns_len.max(ss_len);
    
            let mut selected = Vec::new();
            for idx in 0..max_len {
                let pair = Pair {
                    n: &ns[idx % ns_len],
                    s: &ss[idx % ss_len],
                };
                selected.push(pair);
            }
    
            Pairs {
                // selected: selected, // 以下の4つのエラーが出てコンパイルできない
                // // error[E0515]: cannot return value referencing function parameter `ss`
                // // error[E0515]: cannot return value referencing function parameter `ns`
                // // error[E0505]: cannot move out of `ns` because it is borrowed
                // // error[E0505]: cannot move out of `ss` because it is borrowed
                selected: Vec::new(),
                ns: ns,
                ss: ss,
            }
        }
    
        fn add(&'a mut self) {
            let ns_len = self.ns.len();
            let ss_len = self.ss.len();
            let max_len = ns_len.max(ss_len);
            for idx in 0..max_len {
                let pair = Pair {
                    n: &self.ns[idx % ns_len],
                    s: &self.ss[idx % ss_len],
                };
                self.selected.push(pair);
            }
        }
    
        fn print(&self) {
            for (idx, p) in self.selected.iter().enumerate() {
                println!("pair[{}] : ({}, {})", idx, p.n.n, p.s.s);
            }
            println!();
        }
    }
    
    macro_rules! add_pairs {
        ($pairs:expr) => {
            let ns_len = $pairs.ns.len();
            let ss_len = $pairs.ss.len();
            let max_len = ns_len.max(ss_len);
    
            for idx in 0..max_len {
                let pair = Pair {
                    n: &$pairs.ns[idx % ns_len],
                    s: &$pairs.ss[idx % ss_len],
                };
                $pairs.selected.push(pair);
            }
        };
    }
    
    fn main() {
        // mutをつけても、78行目以降はnsは変更できない。
        #[rustfmt::skip]
        let mut ns = vec![
            N { n: 100 },
            N { n: 101 },
        ];
    
        #[rustfmt::skip]
        let ss = vec![
            S { s: "a".to_string() },
            S { s: "b".to_string() },
            S { s: "c".to_string() },
            S { s: "d".to_string() },
        ];
    
        // 借用される前は変更可能
        ns[0].n = 110;
        ns.push(N { n: 102 });
    
        let mut pairs = Pairs::create_1(ns, ss);
        // pairs.add(); // error[E0502]: cannot borrow `pairs` as immutable because it is also borrowed as mutable
        pairs.print();
    
        // // 以下をできなくするのが目的のためある程度納得。
        // ns[0].n = 0; // error[E0382]: borrow of moved value: `ns`
        // ns.push(N { n: 8 });
    
        // まだ変更できる。
        pairs.ns[1].n = 111;
        pairs.ns.push(N { n: 103 });
    
        // メンバ変数以外から借用する分にはpairs.nsは変更できる。
        let binding = S { s: "z".to_string() };
        pairs.selected.push(Pair {
            n: &N { n: 901 },
            // s: &S { s: "e".to_string() }, // error[E0716]: temporary value dropped while borrowed
            s: &binding,
        });
    
        // まだ変更できるが、selectedにpairs.nsの産所を渡すと変更できなくなる。
        pairs.ns[2].n = 112;
        pairs.ns.push(N { n: 104 });
    
        // Vec<Pair<'a>>自体は変更できる。
        pairs.selected.push(Pair {
            n: &pairs.ns[0],
            s: &pairs.ss[0],
        });
        // pairs.ns[3].n = 113; // error[E0502]: cannot borrow `pairs.ns` as mutable because it is also borrowed as immutable
        // pairs.ns.push(N { n: 105 }); // error[E0502]: cannot borrow `pairs.ns` as mutable because it is also borrowed as immutable
        pairs.print();
    
        add_pairs!(pairs);
        pairs.print();
    }
    
    struct N {
        n: i32,
    }
    
    struct S {
        s: String,
    }
    
    struct Pair<'a> {
        n: &'a N,
        s: &'a S,
    }
    

おそらく他に方法がありそうですが、ひとまずマクロ化してみてみるのはいかがでしょう

macro_rules! vec_pair{
    ($pair_obj:expr)=>{
            for i in 0..$pair_obj.min_len(){
            let pair = Pair::new(&$pair_obj.ns[i], &$pair_obj.ss[i]);
            println!("pair : ({}, {})", pair.n.n, pair.s.s);
            $pair_obj.selected.push(pair);
        }
    }
}

fn main() {
    let ns = vec![
        N { n: 1 },
        N { n: 2 },
        N { n: 3 },
        N { n: 4 },
        N { n: 5 },
        N { n: 6 },
    ];

    let ss = vec![
        S { s: "a".to_string() },
        S { s: "b".to_string() },
        S { s: "c".to_string() },
        S { s: "d".to_string() },
    ];

    let mut pairs = Pairs::new(ns, ss);
    vec_pair!(pairs);

    for (idx, p) in pairs.selected.iter().enumerate() {
            println!("pair[{}] : ({}, {})", idx, p.n.n, p.s.s);
    }
}

C/C++と同じくマクロは置き換えなので、所有権の移動を考えずに呼び出せます
Rust 宣言マクロのスコープ・インポート・エクスポート

struct N {
    n: i32,
}

struct S {
    s: String,
}

struct Pair<'a> {
    n: &'a N,
    s: &'a S,
}

struct Pairs<'a> {
    selected: Vec<Pair<'a>>,
    ns: Vec<N>,
    ss: Vec<S>,
}

impl<'a> Pair<'a> {
    fn new(n: &'a N, s: &'a S) -> Pair<'a> {
        Pair { n, s }
    }
}

impl<'a> Pairs<'a> {
    fn new(ns: Vec<N>, ss: Vec<S>) -> Pairs<'a> {
        Pairs {
            selected: Vec::new(),
            ns,
            ss,
        }
    }

    fn min_len(&self) -> usize {
        let vec_len = vec![self.ns.len(), self.ss.len()];
        *vec_len.iter().min().unwrap()
    }

    fn make_pairs(&'a mut self) {
        for i in 0..self.min_len() {
            let pair = Pair::new(&self.ns[i], &self.ss[i]);
            println!("pair : ({}, {})", pair.n.n, pair.s.s);
            self.selected.push(pair);
        }
    }
}

macro_rules! vec_pair{
    ($pair_obj:expr)=>{
            for i in 0..$pair_obj.min_len(){
            let pair = Pair::new(&$pair_obj.ns[i], &$pair_obj.ss[i]);
            println!("pair : ({}, {})", pair.n.n, pair.s.s);
            $pair_obj.selected.push(pair);
        }
    }
}

fn main() {
    let ns = vec![
        N { n: 1 },
        N { n: 2 },
        N { n: 3 },
        N { n: 4 },
        N { n: 5 },
        N { n: 6 },
    ];

    let ss = vec![
        S { s: "a".to_string() },
        S { s: "b".to_string() },
        S { s: "c".to_string() },
        S { s: "d".to_string() },
    ];

    let mut pairs = Pairs::new(ns, ss);
    vec_pair!(pairs);

    for (idx, p) in pairs.selected.iter().enumerate() {
            println!("pair[{}] : ({}, {})", idx, p.n.n, p.s.s);
    }
}
pair : (1, a)
pair : (2, b)
pair : (3, c)
pair : (4, d)
pair[0] : (1, a)
pair[1] : (2, b)
pair[2] : (3, c)
pair[3] : (4, d)
1Like

Comments

  1. @tagawa0525

    Questioner

    ありがとうございます。動くことは確認できました。
    コンパイル時にマクロが展開されるなら、今回は関数の代わりにマクロを使用すればコンパイルが通るのは納得しました。
    初めてマクロを定義しましたが、マクロの定義(macro_rules!)自体がマクロなのはメタメタしていて面白いですね。

Your answer might help someone💌