6 août 2025•6 min

String vs. &str – Lequel Utiliser et Quand ?

m
mayo

Comprendre la différence entre String et str est fondamental pour une gestion efficace de la mémoire.

Données Owned vs. Borrowed String ptr | len | cap owned, mutable, extensible 3 mots sur le stack &str ptr | len vue borrowed, immutable fat pointer, 2 mots Octets UTF-8 buffer heap, array stack, ou .rodata

String et str, côte à côte

String str (habituellement &str)
String UTF-8 extensible, heap-allocated Vue immutable, taille fixe dans string UTF-8
Type owned (gère sa mémoire) Type borrowed (ne possède pas la mémoire)
Mutable (peut modifier le contenu) Vue immutable
Créé avec String::from("...") ou "...".to_string() Depuis string literals ("hello") ou emprunté depuis String (&my_string)

Layout mémoire

String : Stocke les données sur la heap avec trois composants :

  • Pointeur vers buffer heap
  • Length (taille actuelle)
  • Capacity (taille allouée)

&str : Un "fat pointer" contenant :

  • Pointeur vers données string (heap, stack, ou mémoire static)
  • Length de la slice

Cas d'usages

Utilise String quand :

  • Tu as besoin de modifier ou faire grandir la string
  • Tu as besoin d'ownership (ex : retourner depuis une fonction)
  • Construire des strings dynamiquement
let mut owned = String::from("hello");
owned.push_str(" world");  // Mutation nécessite String

Utilise &str quand :

  • Tu n'as besoin que d'une vue read-only d'une string
  • Travailler avec des paramètres de fonction (évite les allocations inutiles)
  • Gérer des string literals (stockées en mémoire read-only)
fn process_str(s: &str) -> usize {
    s.len()  // Accès read-only
}

Exemple : Ownership vs Borrowing

fn process_string(s: String) { /* prend ownership */ }
fn process_str(s: &str)      { /* emprunte */ }

fn main() {
    let heap_str = String::from("hello");
    let static_str = "world";
    
    process_string(heap_str);  // Ownership moved
    process_str(static_str);   // Borrowed
    
    // heap_str plus accessible ici
    // static_str encore accessible
}

Considérations de performance

Paramètres de Fonction :

// Inefficace - force l'allocation
fn bad(s: String) -> usize { s.len() }

// Efficace - accepte String et &str
fn good(s: &str) -> usize { s.len() }

// Exemple:
let owned = String::from("test");
good(&owned);     // Deref coercion: String -> &str
good("literal");  // &str direct
Quels appelants peuvent atteindre ta fonction ? fn good(s: &str) String::from("test") passé en &owned "literal" déjà un &'static str &str deref coercion zéro allocation fn good(s: &str) fn bad(s: String) "literal" .to_string() alloc heap forcée fn bad(s: String)

Allocation Mémoire :

  • String alloue sur heap, nécessite désallocation
  • &str vers literals pointe vers le binaire du programme (zero allocation)
  • &str depuis String partage l'allocation existante

Patterns courants

Renvoyer des "Owned" :

fn build_message(name: &str) -> String {
    format!("Hello, {}!", name)  // Retourne String owned
}

Paramètre d'entrée flexible :

fn analyze(text: &str) -> usize {
    // Fonctionne avec inputs String et &str
    text.chars().count()
}

Éviter les Clones Inutiles :

// Mauvais - allocation inutile
fn process_bad(s: &str) -> String {
    s.to_string()  // Seulement si tu as vraiment besoin de données owned
}

// Bon - travaille avec données borrowed quand possible
fn process_good(s: &str) -> &str {
    s.trim()  // Retourne slice de l'original
}

Lequel écrire

  • String : Owned, mutable, heap-allocated
  • str : Borrowed, immutable, flexible (heap/stack/static)
    Préfère &str pour les paramètres de fonction sauf si tu as besoin d'ownership ou mutation

.to_string() alloue dans les deux cas, mais pas pour la même raison : sur un littéral il copie depuis les données en lecture seule du binaire, sur un String il clone un buffer heap existant.

  1. Étape 13 sur 59•7 min
    Comment Rust gère-t-il les string literals (&str) en termes d'allocation mémoire ? Où vivent-elles ?

    Les string literals vivent dans les données en lecture seule du binaire avec une lifetime 'static — ce que ça implique pour l'allocation et la copie.

  2. Étape 14 sur 59•6 min
    Pourquoi &str Ne Rentre Pas dans &String en Rust: Fixes Sympas pour les String Mismatches !
  3. Étape 15 sur 59•6 min
    Quel est le but de Box<T> en Rust ?
Retour au blog
Partager ::