Yuvalanmış bir dizi dizininde “değişmez olarak ödünç verilemez çünkü aynı zamanda değişken olarak ödünç alınamaz” ne demektir?


16

Bu durumda hata ne anlama gelir:

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
    v[v[1]] = 999;
}
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:3:7
  |
3 |     v[v[1]] = 999;
  |     --^----
  |     | |
  |     | immutable borrow occurs here
  |     mutable borrow occurs here
  |     mutable borrow later used here

İndekslemenin Indexve IndexMutözellikleri yoluyla uygulandığını ve bunun v[1]sözdizimsel şeker olduğunu buldum *v.index(1). Bu bilgi ile donatılmış, ben aşağıdaki kodu çalıştırmak için çalıştı:

use std::ops::{Index, IndexMut};

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
    *v.index_mut(*v.index(1)) = 999;
}

Şaşırtıcı bir şekilde, bu kusursuz çalışıyor! İlk snippet neden çalışmıyor, ikincisi çalışıyor? Belgeleri anlama şeklim eşdeğer olmalı, ancak bu kesinlikle doğru değil.


2
Code Advent ile Pas öğrenmek? StackOverflow'a hoş geldiniz ve harika soru için teşekkürler!
Sven Marnach

Tam ; ) Bu benim 3.
yılım

@LucasBoucke Bu komik, genellikle projem için Rust kullanıyorum, ancak bu AoC'yi Haskell'de yazıyorum. Her ikisi de alan adlarında harika dillerdir.
Boiethios

Yanıtlar:


16

Desugared sürümü sahip olduğunuzdan biraz farklı. Çizgi

v[v[1]] = 999;

aslında desugarlar

*IndexMut::index_mut(&mut v, *Index::index(&v, 1)) = 999;

Bu aynı hata iletisiyle sonuçlanır, ancak ek açıklamalar neler olduğuna dair bir ipucu verir:

error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:7:48
  |
7 |     *IndexMut::index_mut(&mut v, *Index::index(&v, 1)) = 999;
  |      ------------------- ------                ^^ immutable borrow occurs here
  |      |                   |
  |      |                   mutable borrow occurs here
  |      mutable borrow later used by call

Desugared versiyonunuzdaki önemli fark değerlendirme sırasıdır. Bir işlev çağrısının bağımsız değişkenleri, aslında işlev çağrısı yapılmadan önce, soldan sağa listelenen sırada değerlendirilir. Bu durumda bu &mut v, öncelikle borçlanmanın mutabil olarak değerlendirildiği anlamına gelir v. Sonra, Index::index(&v, 1)değerlendirilmelidir, ancak bu mümkün değildir - vzaten mutably ödünç alınmıştır. Son olarak, derleyici işlev çağrısı için değiştirilebilir başvurunun hala gerekli olduğunu gösterir index_mut(), bu nedenle paylaşılan başvuru denendiğinde değiştirilebilir başvuru hala canlıdır.

Aslında derleyen sürümün biraz farklı bir değerlendirme sırası vardır.

*v.index_mut(*v.index(1)) = 999;

İlk olarak, yöntem çağrılarına yönelik işlev argümanları soldan sağa, yani *v.index(1)önce değerlendirilir. Bu a ile sonuçlanır usizeve geçici ortak borcu vtekrar serbest bırakılabilir. Daha sonra, alıcısı index_mut()değerlendirilir, yani vmutabakatla ödünç alınır. Paylaşılan borç zaten sonlandırıldığından ve tüm ifade borç kontrolünden geçtiği için bu işe yarar.

Sadece "sürümsel olmayan yaşam" lansmanından bu yana derleme sürümü bunu unutmayın. Rust'un önceki sürümlerinde, paylaşılan borç ifadenin sonuna kadar yaşayacak ve benzer bir hatayla sonuçlanacaktır.

Bence en temiz çözüm geçici bir değişken kullanmaktır:

let i = v[1];
v[i] = 999;

Vay! Burada çok şey oluyor! Açıklamak için zaman ayırdığınız için teşekkür ederiz! (ilginç bir şekilde bu tür “tuhaflıklar” bir dili benim için daha ilginç kılıyor ...). Belki de iç verinin artık gerekli olmadığını anlayabildiği *v.index_mut(*v.index_mut(1)) = 999;gibi, derleyici olmamalı, neden "v'yi bir kereden fazla değiştirilebilir olarak ödünç alamazsınız" şeklinde bir ipucu verebilir misiniz *v.index_mut(*v.index(1)) = 999;?
Lucas Boucke

@LucasBoucke Rust, bazen biraz rahatsız edici olan birkaç tuhaflığa sahiptir, ancak çoğu durumda çözüm, bu durumda olduğu gibi oldukça basittir. Kod hala oldukça okunabilir, aslında sahip olduğunuzdan biraz farklı , bu yüzden pratikte çok önemli değil.
Sven Marnach

@LucasBoucke Üzgünüz, düzenlemenizi şimdiye kadar görmedim. Sonucu *v.index(1)olan değeri o dizinde depolanır ve bu değer ödünç tutmak gerekmez vhayatta. Sonucu *v.index_mut(1), diğer taraftan, bir olan değişken yer ifadesi bu nedenle hayatta ödünç tutmak yapar teorik olarak atanabilir. Yüzeyde, ödünç denetleyiciye, değer ifadesi bağlamında bir yer ifadesinin bir değer ifadesi olarak ele alınabileceğini öğretmek mümkün olmalıdır, bu nedenle bu, Rust'un gelecekteki bazı versiyonlarında derlenebilecektir.
Sven Marnach

Bunu yapmak için bir RFC'ye ne dersiniz:{ let index = *Index::index(&v, 1); let value = 999; *IndexMut::index_mut(&mut v, index) = value; }
Boiethios

@FrenchBoiethios Bunu nasıl resmileştireceğine dair hiçbir fikrim yok ve eminim asla uçmayacak. Bunu ele almak istiyorsanız, gördüğüm tek yol ödünç denetleyicideki iyileştirmelerdir, örneğin, değişebilir borçlanmanın daha sonra başlayabileceğini algılamaktır, çünkü bu gerçekten erkenden gerekli değildir. (Bu özel fikir muhtemelen işe yaramıyor.)
Sven Marnach
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.