Reputation: 3770
I am using the git2 crate and would like to get Statuses
for the repo and store this in my app struct for reuse later since it is expensive to create. The problem is that Statuses
references the Repository
from which it was created. As far as I understand from this question: Why can't I store a value and a reference to that value in the same struct?, I can't return an owned item along with a refence to it since the address of the owned item will change when it is returned from the function and moved, thereby making the reference invalid. The below is a minimal example of what I am trying to do, what is the correct way to tackle this?
use git2::{Repository, Statuses};
struct App<'a> {
repo: Repository,
statuses: Statuses<'a>,
}
impl<'a> App<'a> {
fn new() -> Self {
let repo = Repository::open("myrepo").unwrap();
let statuses = repo.statuses(None).unwrap();
App { repo, statuses }
}
}
fn main() {
let mydata = App::new();
dbg!(mydata.statuses.len());
}
Below is the only solution I have found (also taken from the above question), which is to make Statuses
optional and mutate the app data after Repository
has already been returned from ::new()
. This seems hacky and not idiomatic, and doesn't compile anyway.
use git2::{Repository, Statuses};
struct App<'a> {
repo: Repository,
statuses: Option<Statuses<'a>>,
}
impl<'a> App<'a> {
fn new() -> Self {
let repo = Repository::open("myrepo").unwrap();
App {
repo,
statuses: None,
}
}
}
fn main() {
let mut mydata = App::new();
mydata.statuses = mydata.repo.statuses(None).ok();
dbg!(mydata.statuses.unwrap().len());
}
error[E0597]: `mydata.repo` does not live long enough
--> src/main.rs:19:23
|
19 | mydata.statuses = mydata.repo.statuses(None).ok();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
20 | dbg!(mydata.statuses.unwrap().len());
21 | }
| -
| |
| `mydata.repo` dropped here while still borrowed
| borrow might be used here, when `mydata` is dropped and runs the destructor for type `App<'_>`
EDIT: Some additional context:
I am making an app with egui, so the App struct is the application state. Amoung other things, it will list all git repos in a directory, and display information about their statuses. I measured the repo.statuses(None).unwrap()
call and and for ~10 repositories it took a total of 4ms, so too slow to call on each loop of the app. The obvious solution I could think of was to store the data in the app's state (App) but that doesn't seem possible so I'm looking for alternative approaches.
Upvotes: 0
Views: 109
Reputation: 22601
I think there are two solutions:
Statuses
object and then release itself_cell
to create a self-referential object. Note that this object can then no longer provide mut
access to the Repository
.I'd argue that the first option would be the way to go in your case, because to my understanding, Statuses
is simply a collection of paths with a status for each path.
use std::collections::HashMap;
use git2::{Repository, Status};
struct App {
repo: Repository,
statuses: HashMap<String, Status>,
}
impl App {
fn new() -> Self {
let repo = Repository::open(".").unwrap();
let statuses = repo
.statuses(None)
.unwrap()
.iter()
.map(|el| (el.path().unwrap().to_string(), el.status()))
.collect::<HashMap<_, _>>();
App { repo, statuses }
}
}
fn main() {
let mydata = App::new();
dbg!(mydata.statuses.len());
println!("{:#?}", mydata.statuses);
}
[src/main.rs:24] mydata.statuses.len() = 2
{
"Cargo.toml": WT_MODIFIED,
"src/main.rs": INDEX_MODIFIED | WT_MODIFIED,
}
Upvotes: 1