C ++ ma trzy sposoby przekazywania parametrów do funkcji: według wartości, przez odwołanie do wartości i przez odwołanie do wartości. Przekazywanie wartości tworzy własność w tym sensie, że wywoływana funkcja otrzymuje własną kopię, a przekazywanie przez odwołanie do wartości wskazuje, że wartość może zostać zużyta, tj. Nie będzie już używana przez program wywołujący. Przekazywanie przez odwołanie do wartości oznacza, że obiekt jest tymczasowo zapożyczony od dzwoniącego.
Jednak są one zwykle „umowne” i nie zawsze mogą być sprawdzane przez kompilator. I możesz przypadkowo przekształcić odwołanie do wartości w odwołanie do wartości za pomocą std::move()
. Konkretnie istnieją trzy problemy:
Odwołanie może przeżyć obiekt, do którego się odwołuje. Dożywotni system Rust zapobiega temu.
W dowolnym momencie może być aktywnych więcej niż jedno zmienne / niestałe odniesienie. Sprawdzanie pożyczek Rdza zapobiega temu.
Nie możesz zrezygnować z referencji. Nie można zobaczyć w witrynie wywoławczej, czy ta funkcja tworzy odniesienie do obiektu, bez znajomości podpisu wywoływanej funkcji. Dlatego nie można w niezawodny sposób zapobiegać referencjom, nie usuwając żadnych specjalnych metod zajęć ani nie kontrolując strony wywoławczej pod kątem zgodności z niektórymi przewodnikami stylu „brak referencji”.
Problem życia dotyczy podstawowego bezpieczeństwa pamięci. Używanie odwołania jest oczywiście nielegalne po wygaśnięciu wskazanego obiektu. Ale bardzo łatwo jest zapomnieć o czasie życia, gdy przechowujesz referencję w obiekcie, w szczególności gdy obiekt ten wykracza poza obecny zakres. System typu C ++ nie może tego uwzględnić, ponieważ w ogóle nie modeluje czasów życia obiektów.
std::weak_ptr
Inteligentny wskaźnik ma semantykę własności kodują podobne do zwykłego odniesienia, ale wymaga, aby odwoływać obiekt jest zarządzany za pośrednictwem shared_ptr
, czyli jest odniesienie liczona. To nie jest abstrakcja o zerowym koszcie.
Podczas gdy C ++ ma stały system, nie śledzi to, czy obiekt można zmodyfikować, ale śledzi, czy obiekt można zmodyfikować za pomocą tego konkretnego odwołania . Nie zapewnia to wystarczających gwarancji dla „nieustraszonej współbieżności”. Natomiast Rust gwarantuje, że jeśli istnieje aktywne zmienne odwołanie, które jest jedynym odniesieniem („Jestem jedynym, który może zmienić ten obiekt”), a jeśli istnieją niemodyfikowalne odniesienia, wówczas wszystkie odniesienia do obiektu są niemodyfikowalne („Chociaż mogę czytać z obiektu, nikt nie może go zmienić”).
W C ++ możesz ulec pokusie, aby strzec dostępu do obiektu za pomocą inteligentnego wskaźnika z muteksem. Ale jak omówiono powyżej, gdy już mamy odniesienie, może on uniknąć oczekiwanego czasu życia. Dlatego taki inteligentny wskaźnik nie może zagwarantować, że jest to pojedynczy punkt dostępu do zarządzanego obiektu. Taki schemat może faktycznie zadziałać w praktyce, ponieważ większość programistów nie chce sabotować siebie, ale z punktu widzenia systemu typów jest to nadal całkowicie niesłuszne.
Ogólny problem z inteligentnymi wskaźnikami polega na tym, że są one bibliotekami nad językiem podstawowym. Zestaw podstawowych funkcji językowych umożliwia inteligentne wskaźniki, np. std::unique_ptr
Wymaga konstruktorów ruchów. Ale nie mogą naprawić braków w podstawowym języku. Zdolności do niejawnego tworzenia referencji podczas wywoływania funkcji i posiadania zwisających referencji razem oznaczają, że podstawowy język C ++ jest niesłyszalny. Brak możliwości ograniczenia zmiennych odniesień do jednego oznacza, że C ++ nie może zagwarantować bezpieczeństwa przed warunkami wyścigowymi przy żadnej współbieżności.
Oczywiście pod wieloma względami C ++ i Rust są bardziej do siebie podobne niż do innych, w szczególności jeśli chodzi o ich koncepcje statycznie określonych żywotności obiektów. Ale chociaż możliwe jest pisanie poprawnych programów C ++ (pod warunkiem, że żaden z programistów nie popełnia błędów), Rust gwarantuje poprawność omawianych właściwości.