🦀
Intermediaire 20 questions Rust

Questions Rust

20 questions Rust pour les entretiens. Ownership, borrowing, lifetimes, traits et concurrence sans data races.

1. Qu'est-ce que le systeme d'ownership en Rust et pourquoi est-il important ?

L'ownership est le systeme central de gestion memoire de Rust, verifie a la compilation sans garbage collector. Trois regles : chaque valeur a un seul proprietaire, il ne peut y avoir qu'un proprietaire a la fois, quand le proprietaire sort du scope la valeur est liberee (drop). Le move transfere la propriete (l'ancien proprietaire ne peut plus utiliser la valeur). Le clone fait une copie profonde. Les types implementant le trait Copy (scalaires, tuples de Copy types) sont copies automatiquement au lieu d'etre moves. Ce systeme elimine les use-after-free, les double-free et les data races a la compilation, sans cout runtime.

2. Expliquez les references et le borrowing en Rust.

Le borrowing permet d'utiliser une valeur sans en prendre la propriete. Deux types : reference partagee (&T, lecture seule, multiples simultanees autorisees) et reference mutable (&mut T, lecture-ecriture, une seule a la fois). La regle fondamentale : on peut avoir soit N references partagees, soit une seule reference mutable, jamais les deux. Le borrow checker du compilateur verifie ces regles a la compilation. Les lifetimes ('a) annotent la duree de validite des references quand le compilateur ne peut pas l'inferer automatiquement. Elision rules : dans la plupart des cas, les lifetimes sont inferees sans annotation explicite.

3. Qu'est-ce que les traits en Rust et comment fonctionnent-ils ?

Les traits definissent un comportement partage, similaire aux interfaces. Un trait declare des methodes (avec ou sans implementation par defaut). Les types implementent les traits avec impl Trait for Type. Les trait bounds contraignent les generiques : fn print<T: Display>(val: T). Syntaxe alternative : fn print(val: impl Display). Les trait objects (dyn Trait) permettent le dispatch dynamique via vtable (polymorphisme runtime). Traits importants de la std : Display, Debug, Clone, Copy, Iterator, From/Into, Deref, Drop. L'orphan rule empeche d'implementer un trait externe sur un type externe, garantissant la coherence.

4. Comment fonctionne le pattern matching en Rust ?

Le pattern matching avec match est exhaustif : tous les cas doivent etre couverts. Patterns : litteraux, variables, wildcards (_), tuples, structs (destructuration), enums, references (&), ranges (1..=5), guards (if condition). if let pour un seul pattern, while let pour des boucles. L'enum Option (Some/None) remplace les valeurs null. L'enum Result<T, E> (Ok/Err) gere les erreurs. L'operateur ? propage automatiquement les erreurs (retourne Err si erreur, deballe Ok sinon). Le pattern matching sur les enums est la maniere idiomatique de gerer tous les cas possibles, et le compilateur previent les oublis.

5. Expliquez les smart pointers en Rust.

Les smart pointers ajoutent des fonctionnalites aux references classiques. Box : allocation sur le heap, propriete unique, taille connue a la compilation pour les types recursifs. Rc : reference counting, propriete partagee, single-thread uniquement, immutable. Arc : comme Rc mais thread-safe (atomic reference counting). RefCell : borrow checking au runtime au lieu de la compilation, permet la mutabilite interieure. Mutex : acces mutable exclusif thread-safe. Combinaisons courantes : Arc<Mutex> pour du partage mutable multi-thread, Rc<RefCell> pour du partage mutable single-thread. Weak casse les cycles de references.

6. Comment gerer la concurrence en Rust ?

Rust garantit la securite de la concurrence a la compilation. Les traits Send (transferable entre threads) et Sync (accessible depuis plusieurs threads) sont implementes automatiquement. Les threads se creent avec std::thread::spawn. Les channels (mpsc) pour la communication inter-threads. Mutex pour l'exclusion mutuelle, RwLock pour plusieurs lecteurs/un ecrivain. Le crate rayon pour le parallelisme de donnees simple (par_iter()). Le crate tokio pour l'asynchrone (async/await, tasks, I/O non-bloquant). L'ownership system empeche les data races a la compilation : on ne peut pas partager une reference mutable entre threads sans synchronisation.

7. Qu'est-ce que les lifetimes et quand faut-il les annoter explicitement ?

Les lifetimes representent la duree pendant laquelle une reference est valide. Le compilateur les infere generalement grace aux regles d'elision. Annotations explicites necessaires quand : une fonction retourne une reference et le compilateur ne peut pas determiner de quel parametre elle provient, ou quand une struct contient des references. Syntaxe : fn longest<'a>(x: &'a str, y: &'a str) -> &'a str signifie que la reference retournee vit au moins aussi longtemps que la plus courte des deux entrees. Le lifetime 'static indique que la reference vit pour toute la duree du programme (ex: string literals). Les lifetimes ne changent pas la duree de vie, elles la declarent au compilateur.

8. Expliquez le systeme de modules et la gestion des crates en Rust.

L'arbre de modules organise le code : crate root (lib.rs ou main.rs) est la racine. mod declare un sous-module (dans un fichier ou un dossier). pub rend les elements publics. use importe les chemins. Visibilite : par defaut prive, pub(crate) visible dans le crate, pub(super) visible dans le module parent. Les crates sont les unites de compilation : binary (executable) ou library. Cargo.toml declare les dependances. Cargo gere la compilation, les tests, la documentation, et la publication sur crates.io. Les workspaces regroupent plusieurs crates liees. Les features de Cargo permettent la compilation conditionnelle de fonctionnalites optionnelles.

9. Comment Rust gere-t-il l'allocation memoire sans garbage collector ?

Rust utilise le systeme RAII (Resource Acquisition Is Initialization). Quand une variable sort du scope, son destructeur (trait Drop) est appele automatiquement, liberant la memoire. La pile (stack) est utilisee pour les donnees de taille connue a la compilation. Le tas (heap) via Box, Vec, String, etc. est libere quand le proprietaire est drop. Pas de fragmentation de pause comme avec un GC. Le borrow checker garantit qu'aucune reference ne pointe vers de la memoire liberee (dangling pointer). Les smart pointers (Rc, Arc) utilisent le reference counting pour la propriete partagee. Unsafe Rust permet de gerer manuellement la memoire quand necessaire, mais le programmeur assume la responsabilite.

10. Quelles sont les differences entre les closures et les fonctions en Rust ?

Les closures capturent des variables de leur environnement, contrairement aux fonctions. Trois modes de capture : Fn (capture par reference partagee), FnMut (capture par reference mutable), FnOnce (capture par move, consomme les variables). Le compilateur infere automatiquement le mode le moins restrictif. Le mot-cle move force la capture par valeur (utile pour les threads). Les closures ont un type anonyme unique (chaque closure a son propre type meme avec la meme signature). On peut les passer comme arguments via les trait bounds (impl Fn(i32) -> i32) ou les trait objects (Box<dyn Fn(i32) -> i32>). Les closures zero-capture peuvent etre converties en pointeurs de fonction (fn).

Besoin d'aide pour preparer vos entretiens ?

Decrivez votre profil pour des conseils de preparation personnalises.

Recevoir des conseils

Questions frequentes

Rust est-il realiste pour un premier emploi ?
Rust est encore niche mais en forte croissance. C'est un excellent atout pour les postes systemes, cloud et blockchain.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.