2017-01-15 23 views
5

Mój program używa rusqlite do zbudowania bazy danych z innego źródła danych. Baza buduje wielu tabel w taki sam sposób, więc pomyślałem, że zrobię wielokrotnego użytku funkcji, aby to zrobić:Problemy z wypożyczaniem ze skompilowanymi instrukcjami SQL

fn download_generic<Inserter>(table_name: &str, 
           connection: &mut rusqlite::Connection, 
           inserter: &mut Inserter) 
           -> Result<(), String> 
    where Inserter: FnMut(&str, &json::JsonValue) ->() 
{} 

inserter to funkcja, która wiąże się prawidłowe wartości z wcześniej przygotowanym oświadczeniu i nie wstawianie .

nazywam go tak:

let mut insert_stmt = connection 
    .prepare("insert or replace into categories values(?,?);") 
    .unwrap(); 

download_generic("categories", 
       &mut connection, 
       &mut |uuid, jsonproperties| { 
        insert_stmt.execute(&[&uuid, &jsonproperties["name"].as_str().unwrap_or("")]); 
       }); 

Jednak nie mogę przejść &mut connection do download_generic bo to już pożyczone przez insert_stmt. Umieszczenie go w RefCell nie ma sensu, ponieważ nie powinienem potrzebować narzutu środowiska wykonawczego, aby to działało.

Mogę spróbować wygenerować insert_stmt wygenerowany przez lambdę, którą przechodzisz na download_generic, ale wtedy jestem przytłoczony koniecznością dodawania znaczników życia wszędzie, i tak czy inaczej wydaje się to nienaturalne.

+0

Umieszczenie go w "RefCell" nie zadziałałoby: 'RefCell' po prostu sprawdza w czasie wykonywania zamiast kompilacji, ale wykonuje to samo podstawowe sprawdzenie => ** Aliasing XOR Mutability **. Oczywiste pytanie brzmi: czy nie możesz po prostu pożyczyć (niezmiennie) tego samego połączenia? Jeśli pojedynczy wymagany kredyt jest zmienny, oznacza to, że nie masz szczęścia. –

+0

Czy nie mogę po prostu przenieść insert_stmt do lambda w jakiś sposób, aby 'download_generic' zachował to? – njaard

+0

Czy zamiast tego możesz użyć "przygotować_cached" zamiast "przygotować", aby uzyskać gotowe oświadczenie w zamknięciu? Musiałbyś jawnie przekazać 'połączenie' do zamknięcia, aby uniknąć konfliktu pożyczek. –

Odpowiedz

2

Zgodnie z projektem, Rust uniemożliwia posiadanie niezmiennego poŜyczki i moŜliwego do zmienienia poŜyczenia na tym samym obiekcie aktywnym w tym samym czasie. Ma to zapobiegać zwisaniu wskaźników i wyścigów danych.

W interfejsie API rusqlite niektóre metody na Connection wymagają zmiennego self, a niektóre metody wymagają tylko niezmiennego self. Jednak niektóre metody wymagają tylko niezmiennych obiektów zwracających, które utrzymują tę pożyczkę aktywną; prepare jest tego przykładem. Dlatego tak długo, jak jeden z tych obiektów pozostaje w zakresie, Rust nie pozwoli ci wziąć zmiennego pożyczyć na Connection.

Prawdopodobnie istnieje jakiś powód, dla którego niektóre metody przyjmują self przez zmienne odniesienie. Wymuszenie odniesienia do zmiennej zapewnia osobie, która ma wyłączny dostęp do tego obiektu. Jeśli uważasz, że nie jest to możliwe w przypadku metod, których potrzebujesz, lub uważasz, że może być inny sposób rozwiązania tego problemu, powinieneś zgłosić problem do opiekunów biblioteki.

W odniesieniu do prepare w szczególności można obejść sprzeczne zaciągnięcia pożyczek, dzwoniąc pod numer prepare_cached z poziomu zamknięcia. Aby to zrobić, musisz przekazać download_generic ponownie jako parametr zamknięcia, w przeciwnym razie masz dwa zmienne zapożyczenia na connection, a to nie jest dozwolone.

+0

'Connection :: prepare' akceptuje niezmienny' & self', a więc mogę przekazać przygotowane 'Statement' wraz z samym' Connection'. W związku z tym problem ten można rozwiązać banalnie. – njaard

Powiązane problemy