possibile ottenere il modello di proprietà di Rust con un wrapper C ++ generico?


15

Guardando questo articolo sulla sicurezza della concorrenza di Rust:

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

Mi chiedevo quante di queste idee possano essere realizzate in C ++ 11 (o più recenti). In particolare, posso creare una classe proprietario che trasferisce la proprietà a qualsiasi metodo a cui può essere passata? Sembra che C ++ abbia così tanti modi per passare le variabili che sarebbe impossibile, ma forse potrei mettere alcune restrizioni sulla classe o sul modello per garantire che un codice modello venga eseguito con ogni passaggio del metodo?


Alcune citazioni dal link migliorerebbero questa domanda
Martin Ba,

2
@delnan (Sicuro) Rust garantisce che non hai mai più di un riferimento mutabile a qualcosa alla volta e che non hai mai un riferimento mutabile a qualcosa a cui stai anche facendo riferimento di sola lettura. Ha anche alcune restrizioni sul trasferimento di dati tra thread. Insieme, questi impediscono una significativa classe di bug relativi al threading e facilitano il ragionamento sullo stato degli oggetti, anche nel codice a thread singolo.
CodesInChaos,

3
Non pensi di poter esprimere il prestito in un modo che il compilatore C ++ potrebbe verificare, quindi dovresti ricorrere all'applicazione del runtime con l'hit performance associato.
CodesInChaos,

1
Le proprietà dell'ambito non sono già implementate dai puntatori intelligenti in C ++ 11?
Akshat Mahajan,

1
@JerryJeremiah Rust ha una grande varietà di tipi di riferimento. Quelle di base, &non richiedono alcun tipo di promozione da utilizzare. Se cerchi di ottenere un &mutpo 'di tempo hai ancora un altro riferimento (mutabile o no) allo stesso oggetto, non sarai in grado di compilare. RefCell<T>sposta il controllo sul tempo di esecuzione, quindi otterrai un panico se provi a .borrow_mut()qualcosa che ha già un attivo .borrow()o .borrow_mut(). Rust ha anche Rc<T>(puntatore proprietario condiviso) e suo fratello Weak<T>, ma quelli riguardano la proprietà, non la mutabilità. Mettici RefCell<T>dentro per mutabilità.
settembre 17-17

Risposte:


8

C ++ ha tre modi per passare i parametri a una funzione: per valore, per riferimento lvalue e per riferimento rvalue. Di questi, il passaggio in base al valore crea la proprietà nel senso che la funzione chiamata riceve la propria copia e il passaggio in base al valore di riferimento indica che il valore può essere consumato, ovvero non verrà più utilizzato dal chiamante. Il passaggio per riferimento lvalore indica che l'oggetto è temporaneamente preso in prestito dal chiamante.

Tuttavia, questi tendono ad essere "per convenzione" e non possono sempre essere controllati dal compilatore. E puoi accidentalmente trasformare un riferimento al valore in un riferimento al valore usandostd::move() . Concretamente, ci sono tre problemi:

  • Un riferimento può sopravvivere all'oggetto a cui fa riferimento. Il sistema a vita di Rust lo impedisce.

  • Possono essere attivi più riferimenti mutabili / non cost in qualsiasi momento. Il correttore di prestito di Rust lo impedisce.

  • Non è possibile rinunciare ai riferimenti. Non è possibile vedere in un sito di chiamata se tale funzione crea un riferimento al proprio oggetto, senza conoscere la firma della funzione chiamata. Pertanto non è possibile prevenire in modo affidabile riferimenti, né eliminando metodi speciali delle proprie classi né controllando il sito di chiamata per la conformità con alcune guide di stile "senza riferimenti".

Il problema della durata riguarda la sicurezza di base della memoria. È ovviamente illegale utilizzare un riferimento quando l'oggetto di riferimento è scaduto. Ma è molto facile dimenticare la durata in cui si memorizza un riferimento all'interno di un oggetto, in particolare quando quell'oggetto sopravvive all'ambito corrente. Il sistema di tipo C ++ non può spiegarlo perché non modella affatto la durata degli oggetti.

Il std::weak_ptrpuntatore intelligente codifica la semantica della proprietà simile a un semplice riferimento, ma richiede che l'oggetto a cui viene fatto riferimento sia gestito tramite ashared_ptr , ovvero contato come riferimento. Questa non è un'astrazione a costo zero.

Mentre C ++ ha un sistema const, questo non tiene traccia della possibilità di modificare un oggetto, ma della possibilità di modificare un oggetto attraverso quel particolare riferimento . Ciò non fornisce garanzie sufficienti per una "concorrenza senza paura". Al contrario, Rust garantisce che se esiste un riferimento mutabile attivo che è l'unico riferimento ("Sono l'unico che può cambiare questo oggetto") e se ci sono riferimenti non mutabili, tutti i riferimenti all'oggetto non sono mutabili ("Mentre posso leggere l'oggetto, nessuno può cambiarlo").

In C ++ potresti essere tentato di proteggere l'accesso a un oggetto tramite un puntatore intelligente con un mutex. Ma come discusso sopra una volta che abbiamo un riferimento, può sfuggire alla sua vita prevista. Pertanto un tale puntatore intelligente non può garantire che sia il singolo punto di accesso al suo oggetto gestito. Un tale schema potrebbe effettivamente funzionare nella pratica perché la maggior parte dei programmatori non vuole sabotare se stessa, ma dal punto di vista del sistema di tipi questo è ancora del tutto infondato.

Il problema generale con i puntatori intelligenti è che sono librerie in cima al linguaggio principale. L'insieme delle funzionalità del linguaggio di base abilita questi suggerimenti intelligenti, ad esempio ha std::unique_ptrbisogno di costruttori di mosse. Ma non possono correggere le carenze nella lingua principale. Le capacità di creare implicitamente riferimenti quando si chiama una funzione e di avere riferimenti penzolanti insieme significano che il linguaggio C ++ di base è insensato. L'incapacità di limitare i riferimenti mutabili a uno solo significa che C ++ non può garantire sicurezza contro le condizioni di gara con qualsiasi tipo di concorrenza.

Naturalmente per molti aspetti C ++ e Rust sono più simili di quanto non siano, in particolare per quanto riguarda i loro concetti di durata dell'oggetto determinata staticamente. Ma mentre è possibile scrivere programmi C ++ corretti (purché nessuno dei programmatori commetta errori), Rust garantisce la correttezza delle proprietà discusse.


Se il problema è che il C ++ non tiene traccia della proprietà nel linguaggio principale, sarebbe possibile implementare tale funzionalità attraverso la meta-programmazione? Ciò significa che dovresti creare una nuova classe di puntatori intelligenti che sarebbe sicura per la memoria (1) costringendola a puntare esclusivamente su oggetti che usano solo puntatori intelligenti della stessa classe e (2) tenere traccia della proprietà attraverso i modelli
Elliot Gorokhovsky

2
@ElliotGorokhovsky No, perché il modello non può disabilitare le funzioni del linguaggio principale come i riferimenti. Un puntatore intelligente può rendere più difficile ottenere un riferimento, ma a quel punto stai combattendo la lingua - la maggior parte delle funzioni di libreria standard hanno bisogno di riferimenti. Inoltre, non è possibile controllare la durata di un riferimento attraverso modelli perché la lingua non offre un concetto reificato di durata.
amon,

Vedo, grazie
Elliot Gorokhovsky,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.