możliwe jest osiągnięcie modelu własności Rust za pomocą ogólnego opakowania C ++?

15

Przeglądając ten artykuł na temat bezpieczeństwa współbieżności Rust:

http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html

Zastanawiałem się, ile z tych pomysłów można osiągnąć w C ++ 11 (lub nowszym). W szczególności czy mogę utworzyć klasę właściciela, która przenosi własność na dowolną metodę, na którą może zostać przekazana? Wygląda na to, że C ++ ma tak wiele sposobów przekazywania zmiennych, że byłoby to niemożliwe, ale może mógłbym wprowadzić pewne ograniczenia dla klasy lub szablonu, aby zapewnić wykonanie kodu szablonu przy każdym przejściu metody?

Brannon
źródło
Niektóre cytaty z linku poprawiłyby to pytanie
Martin Ba
2
@delnan (Safe) Rust gwarantuje, że nigdy nie masz więcej niż jednego zmiennego odniesienia do czegoś na raz i że nigdy nie masz zmiennego odniesienia do rzeczy, do której masz również odniesienia tylko do odczytu. Ma również pewne ograniczenia dotyczące przesyłania danych między wątkami. Razem zapobiegają znacznej liczbie błędów związanych z wątkami i ułatwiają rozumowanie o stanie obiektów, nawet w kodzie jednowątkowym.
CodesInChaos
3
Nie sądzisz, że możesz wyrazić pożyczanie w sposób, który mógłby zweryfikować kompilator C ++, więc będziesz musiał uciekać się do wymuszania w czasie wykonywania z powiązanym spadkiem wydajności.
CodesInChaos
1
Czy własności zakresu nie są już zaimplementowane przez inteligentne wskaźniki w C ++ 11?
Akshat Mahajan
1
@JerryJeremiah Rust ma wiele różnych typów referencji. Te podstawowe &nie wymagają użycia żadnej promocji. Jeśli spróbujesz uzyskać trochę &mutczasu, nadal masz inne odwołanie (zmienne lub nie) do tego samego elementu, nie będziesz w stanie skompilować. RefCell<T>przesuwa czek do czasu wykonywania, więc wpadniesz w panikę, jeśli spróbujesz .borrow_mut()czegoś, co już ma aktywne .borrow()lub .borrow_mut(). Rdza ma również Rc<T>(wspólny wskaźnik posiadania) i jego rodzeństwo Weak<T>, ale te dotyczą własności, a nie zmienności. Wstaw do RefCell<T>środka, aby uzyskać zmienność.
8bittree,

Odpowiedzi:

8

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_ptrInteligentny 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_ptrWymaga 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.

amon
źródło
Jeśli problem polega na tym, że C ++ nie śledzi własności w języku podstawowym, czy byłoby możliwe zaimplementowanie tej funkcji za pomocą metaprogramowania? Oznacza to, że utworzyłbyś nową inteligentną klasę wskaźników, która byłaby bezpieczna dla pamięci poprzez (1) zmuszanie jej do wskazywania wyłącznie obiektów, które używają tylko inteligentnych wskaźników z tej samej klasy i (2) śledzenia własności za pośrednictwem szablonów
Elliot Gorokhovsky
2
@ElliotGorokhovsky Nie, ponieważ szablon nie może wyłączyć podstawowych funkcji języka, takich jak odwołania. Inteligentny wskaźnik może utrudnić uzyskanie referencji, ale w tym momencie walczysz z językiem - większość standardowych funkcji bibliotecznych potrzebuje referencji. Nie można również sprawdzić żywotności referencji za pomocą szablonów, ponieważ język nie oferuje zmienionej koncepcji życia.
amon
Rozumiem, dziękuję
Elliot Gorokhovsky