J'ai un dépôt Git caché derrière un Mutex
:Comment puis-je remplacer la valeur dans un Mutex?
pub struct GitRepo {
contents: Mutex<GitContents>,
workdir: PathBuf,
}
Je veux l'interroger, mais seulement un maximum de fois: après qu'il a été interrogé, je veux simplement utiliser les résultats que nous avons obtenu la première fois. Un référentiel a un git2::Repository
ou un vecteur de résultats. Un Repository
est Send
mais pas Sync
.
enum GitContents {
Before { repo: git2::Repository },
After { statuses: Git },
}
struct Git {
statuses: Vec<(PathBuf, git2::Status)>,
}
Le GitContents
ENUM reflète le fait que nous avons soit le dépôt d'une requête, ou les résultats de l'interrogation, mais jamais les deux.
J'essaie d'obtenir Rust pour faire respecter cette propriété en ayant la fonction qui transforme un référentiel en états consomment le référentiel tel qu'il produit le vecteur d'état:
fn repo_to_statuses(repo: git2::Repository, workdir: &Path) -> Git {
// Assume this does something useful...
Git { statuses: Vec::new() }
}
Cependant, je ne peux pas obtenir le Mutex
pour jouer sympa avec cela. Voici ma tentative à ce jour pour écrire une fonction qui interroge un GitRepo
avec un prédicat P
, en remplaçant la valeur à l'intérieur du Mutex
si elle n'a pas été encore interrogé:
impl GitRepo {
fn search<P: Fn(&Git) -> bool>(&self, p: P) -> bool {
use std::mem::replace;
// Make this thread wait until the mutex becomes available.
// If it's locked, it's because another thread is running repo_to_statuses
let mut contents = self.contents.lock().unwrap();
match *contents {
// If the repository has been queried then just use the existing results
GitContents::After { ref statuses } => p(statuses),
// If it hasn't, then replace it with some results, then use them.
GitContents::Before { ref repo } => {
let statuses = repo_to_statuses(*repo, &self.workdir);
let result = p(&statuses);
replace(&mut *contents, GitContents::After { statuses });
result
},
}
}
}
Bien qu'il y ait mutation en cause, cette méthode ne prend &self
plutôt que &mut self
car il renvoie le même résultat, que le référentiel soit interrogé pour la première ou la deuxième fois, même si le premier travail est en cours. Mais Rust se plaint:
- Il refuse de déplacer le
repo
sur le contenu que j'ai emprunté àrepo_to_statuses(*repo, &self.workdir)
, même si je sais que la valeur doit se remplacer immédiatement après. ("ne peut pas sortir du contenu emprunté") - Il ne m'aime pas
replace
-ing&mut *contents
soit, parce que j'emprunte le contenu immuablement que la valeur étantmatch
-ed. ("ne peut pas emprunter le contenu" comme mutable parce qu'il est aussi emprunté comme immuable ")
Y a-t-il un moyen de convaincre le vérificateur d'emprunts de mes intentions?
"Vous souhaiterez peut-être ajouter une nouvelle variante enum représentant l'état lorsque tout a été déplacé mais que rien n'a encore été déplacé." C'était la partie que j'avais besoin d'entendre. Je vous remercie! –