Uwaga redaktora : to pytanie zostało zadane przed Rust 1.0, a niektóre stwierdzenia w nim niekoniecznie są prawdziwe w Rust 1.0. Niektóre odpowiedzi zostały zaktualizowane w celu uwzględnienia obu wersji.
Mam tę strukturę
struct Triplet {
one: i32,
two: i32,
three: i32,
}
Jeśli przekażę to do funkcji, jest ona niejawnie kopiowana. Czasami czytam, że niektórych wartości nie da się skopiować i dlatego trzeba je przenieść.
Czy byłoby możliwe uniemożliwienie Triplet
kopiowania tej struktury ? Na przykład, czy byłoby możliwe zaimplementowanie cechy, która uczyniłaby Triplet
niekopiowalną, a zatem „ruchomą”?
Czytałem gdzieś, że trzeba zaimplementować Clone
cechę kopiowania rzeczy, które nie są w sposób dorozumiany kopiowalne, ale nigdy nie czytałem o czymś odwrotnym, czyli o posiadaniu czegoś, co jest niejawnie kopiowalne i uniemożliwiające kopiowanie, aby zamiast tego się poruszało.
Czy to w ogóle ma sens?
Odpowiedzi:
Przedmowa : Ta odpowiedź została napisana przed opt-in wbudowaną cech -specifically te
Copy
aspekty -were realizowanych. Użyłem cudzysłowów blokowych, aby wskazać sekcje, które dotyczyły tylko starego schematu (tego, który był stosowany, gdy zadawano pytanie).Typy są teraz przenoszone domyślnie, to znaczy, gdy definiujesz nowy typ, nie jest on implementowany,
Copy
chyba że jawnie zaimplementujesz go dla swojego typu:Implementacja może istnieć tylko wtedy, gdy każdy typ zawarty w new
struct
lubenum
jest sobąCopy
. Jeśli nie, kompilator wydrukuje komunikat o błędzie. Może również istnieć tylko wtedy, gdy typ nie maDrop
implementacji.Aby odpowiedzieć na pytanie, którego nie zadałeś ... „o co chodzi z przenoszeniem i kopiowaniem?”:
Najpierw zdefiniuję dwie różne „kopie”:
(&usize, u64)
, to 16 bajty na komputerze 64-bitowym, a płytkie kopia będzie przy tych 16 bajtów i replikowania swoich wartość w innym 16-bajtowym fragmencie pamięci, bez dotykaniausize
drugiego końca pliku&
. Oznacza to, że jest to równoważne z dzwonieniemmemcpy
.Rc<T>
wymaga po prostu zwiększenia liczby odniesień, a semantyczna kopia aVec<T>
polega na utworzeniu nowej alokacji, a następnie semantycznym kopiowaniu każdego przechowywanego elementu ze starego do nowego. Mogą to być kopie głębokie (np.Vec<T>
) Lub płytkie (np.Rc<T>
Nie dotykają przechowywanychT
),Clone
są luźno definiowane jako najmniejsza ilość pracy wymagana do semantycznego skopiowania wartości typuT
z wnętrza a&T
doT
.Rust jest podobny do C, każde użycie wartości według wartości jest kopią bajtową:
Są to kopie bajtowe, niezależnie od tego, czy są
T
przenoszone, czy też są „niejawnie kopiowalne”. (Aby było jasne, nie zawsze są to kopie bajt po bajcie w czasie wykonywania: kompilator może optymalizować kopie, jeśli zachowanie kodu jest zachowane.)Istnieje jednak podstawowy problem z kopiami bajtowymi: kończy się to ze zduplikowanymi wartościami w pamięci, co może być bardzo złe, jeśli mają destruktory, np.
Gdyby
w
była to zwykła kopia bajtowa,v
byłyby dwa wektory wskazujące na tę samą alokację, oba z destruktorami, które ją zwalniają ... powodując podwójne zwolnienie , co jest problemem. NB. Byłoby to całkowicie w porządku, gdybyśmy zrobili semantyczną kopię programuv
intow
, ponieważ wtedyw
byłyby jego własne niezależne,Vec<u8>
a destruktory nie deptałyby się nawzajem.Istnieje kilka możliwych poprawek:
w
miała własną alokację, podobnie jak C ++ z konstruktorami kopiowania.v
nie można go już używać i nie można uruchomić jego destruktora.Ostatnim jest to, co robi Rust: ruch to tylko użycie wartości, w którym źródło jest statycznie unieważnione, więc kompilator zapobiega dalszemu używaniu niepoprawnej pamięci.
Typy, które mają destruktory, muszą się przesuwać, gdy są używane przez wartość (inaczej podczas kopiowania bajtu), ponieważ mają zarządzanie / własność jakiegoś zasobu (np. Alokację pamięci lub uchwyt pliku) i jest bardzo mało prawdopodobne, aby kopia bajtowa poprawnie to powieliła własność.
„Cóż… co to jest ukryta kopia?”
Pomyśl o typie pierwotnym, takim jak
u8
: kopia bajtowa jest prosta, po prostu skopiuj pojedynczy bajt, a kopia semantyczna jest równie prosta, skopiuj pojedynczy bajt. W szczególności kopia bajtowa jest kopią semantyczną ... Rust ma nawet wbudowaną cechę,Copy
która przechwytuje, które typy mają identyczne kopie semantyczne i bajtowe.W związku z tym dla tych
Copy
typów zastosowań według wartości są również automatycznie kopiami semantycznymi, więc dalsze korzystanie ze źródła jest całkowicie bezpieczne.Jak wspomniano powyżej, wbudowane cechy opt-in są zaimplementowane, więc kompilator nie ma już automatycznego zachowania. Jednak reguła stosowana w przeszłości do automatycznego zachowania to te same reguły, które służą do sprawdzania, czy ich wdrożenie jest legalne
Copy
.źródło
Najłatwiej jest osadzić w typie coś, czego nie można skopiować.
Biblioteka standardowa zapewnia „typ znacznika” dokładnie dla tego przypadku użycia: NoCopy . Na przykład:
źródło