mem::replace în Rust

Pune o valoare nouă într-un loc mutabil și returnează valoarea veche, mutată afară.

Sintaxă
pub fn replace<T>(dest: &mut T, src: T) -> T

Ce face mem::replace

mem::replace(&mut loc, nou) scrie nou în loc și îți dă înapoi valoarea care era acolo. Este felul de a muta o valoare dintr-un câmp prin &mut self, lucru interzis direct (eroarea cannot move out of ... which is behind a mutable reference).

Dacă valoarea nouă este chiar valoarea implicită a tipului, mem::take este mai scurt.

Parametri

Parametru Descriere
dest Locul modificat, prin &mut.
src Valoarea nouă pusă în dest.

Ce returnează

Valoarea veche din dest.

Exemple

Exemplul 1: Schimbarea stării unui automat

Starea veche este mutată afară ca să putem folosi datele din ea, iar în loc punem starea nouă.

use std::mem;

#[derive(Debug)]
enum Stare {
    Cos(Vec<&'static str>),
    Platita { produse: usize },
}

struct Comanda {
    stare: Stare,
}

impl Comanda {
    fn plateste(&mut self) {
        let vechi = mem::replace(&mut self.stare, Stare::Platita { produse: 0 });
        if let Stare::Cos(produse) = vechi {
            println!("plătesc {} produse: {produse:?}", produse.len());
            self.stare = Stare::Platita { produse: produse.len() };
        }
    }
}

fn main() {
    let mut c = Comanda { stare: Stare::Cos(vec!["carte", "pix"]) };
    println!("înainte: {:?}", c.stare);
    c.plateste();
    println!("după: {:?}", c.stare);
    if let Stare::Platita { produse } = c.stare {
        println!("produse plătite: {produse}");
    }
}

Rezultat

înainte: Cos(["carte", "pix"])
plătesc 2 produse: ["carte", "pix"]
după: Platita { produse: 2 }
produse plătite: 2

Exemplul 2: Valoarea veche, înapoi

use std::mem;

fn main() {
    let mut presedinte = String::from("Ion");
    let fost = mem::replace(&mut presedinte, String::from("Maria"));
    println!("fost: {fost}, acum: {presedinte}");

    let mut scor = 87;
    let vechi = mem::replace(&mut scor, 0);
    println!("scor vechi: {vechi}, scor nou: {scor}");
}

Rezultat

fost: Ion, acum: Maria
scor vechi: 87, scor nou: 0

Greșeli frecvente

  • Clonezi un câmp mare doar ca să-l poți muta (let v = self.date.clone();): mem::replace sau mem::take evită copierea.

Comenzi înrudite

  • mem::take: Mută valoarea afară dintr-un loc mutabil și lasă în loc valoarea implicită a tipului (text gol, vector gol, 0).
  • mem::swap: Schimbă între ele valorile din două locuri mutabile, fără copiere și fără variabilă temporară.
  • Option::take: take scoate valoarea și lasă None în loc; replace pune o valoare nouă și o returnează pe cea veche.