C'est définitivement intéressant.
ils sont similaires - mais pas tout à fait les mêmes. resize()
est un membre de Vec
. rotate_right()
, en revanche, est une méthode des tranches.
Vec
se déréfère en [T]
, donc la plupart du temps cela n'a pas d'importance. Mais en réalité, alors que cet appel :
vec.resize(vec.len(), 0);
Se désucre en quelque chose comme :
>::resize(&mut vec, >::len(&vec), 0);
Cet appel :
vec.rotate_right(vec.len());
Est plus comme :
<[i32]>::rotate_right(
as DerefMut>::deref_mut(&mut vec),
>::len(&vec),
);
Mais dans quel ordre ?
Ceci est le MIR pour rotate_right()
(simplifié beaucoup) :
fn foo() -> () {
_4 = as DerefMut>::deref_mut(move _5);
_6 = Vec::::len(move _7);
_2 = core::slice::::rotate_right(move _3, move _6);
}
Et ceci est le MIR pour resize()
(encore une fois, simplifié beaucoup) :
fn foo() -> () {
_4 = Vec::::len(move _5);
_2 = Vec::::resize(move _3, move _4, const 0_i32);
}
Dans l'exemple de resize()
, nous appelons d'abord Vec::len()
avec une référence à vec
. Cela renvoie usize
. Ensuite, nous appelons Vec::resize()
, lorsque nous n'avons pas d'autres références en suspens à vec
, donc l'emprunt mutable est autorisé !
Cependant, avec rotate_right()
, d'abord nous appelons as DerefMut>::deref_mut(&mut vec)
. Cela renvoie &mut [i32]
, avec sa durée de vie liée à vec
. C'est-à-dire, tant que cette référence (référence mutable !) est active, nous ne sommes pas autorisés à utiliser d'autres références à vec
. Mais ensuite nous essayons d'emprunter vec
afin de passer la référence (partagée, mais cela n'a pas d'importance) à Vec::len()
, alors que nous devons encore utiliser la référence mutable de deref_mut()
plus tard, dans l'appel à <[i32]>::rotate_right()
! C'est une erreur.
Cela est dû au fait que Rust définit un ordre d'évaluation pour les opérandes :
Les expressions prenant plusieurs opérandes sont évaluées de gauche à droite telles qu'elles sont écrites dans le code source.
Parce que vec.resize()
est en fait (&mut *vec).rotate_right()
, nous évaluons d'abord la déréférence + la référence, puis les arguments :
let dereferenced_vec = &mut *vec;
let len = vec.len();
déréférencec_vec.rotate_right(len);
Ce qui est clairement une violation des règles de l'emprunt.
En revanche, vec.resize(vec.len())
n'a aucun travail à faire sur le calle (vec
), et donc nous évaluons d'abord vec.len()
, puis l'appel lui-même.
Résoudre cela est aussi simple que d'extraire le vec.len()
sur une nouvelle ligne (nouvelle instruction, pour être précis), et le compilateur le suggère également.