Cuando pasamos una variable a una función en Rust, la forma en que el compilador gestiona la propiedad (ownership) depende de si el tipo implementa el trait Copy. Para tipos que mueven su propiedad, como String, el valor se transfiere al parámetro de la función, invalidando la variable en el contexto del llamador. Para evitar la pérdida de datos, se utiliza el patrón “move in, move out”: la función consume el valor (move in) y lo devuelve mediante el tipo de retorno (move out), permitiendo que el llamador recupere el control sobre el objeto transformado.
Para realizar operaciones sobre valores que se encuentran detrás de una referencia mutante (&mut T) sin perder su propiedad, se utilizan herramientas como std::mem::replace o std::mem::swap. Estas funciones permiten extraer un valor de una posición de memoria y colocar uno nuevo de forma atómica para el modelo de propiedad, evitando dejar la memoria en un estado inválido o nulo.
Un aspecto crítico es la consistencia en las estructuras de control. El compilador de Rust rastrea el flujo de propiedad a través de las ramas (branches) de un if/else. Si en una rama se mueve el ownership de una variable hacia un nuevo ámbito o se retorna, pero en la otra rama la variable permanece intacta, el compilador exigirá que el resultado final sea consistente. Si intentas usar la variable después de un bloque donde una rama la movió y la otra no, el compilador lanzará un error porque no puede garantizar si la variable es válida o no.
use std::mem;
// Demuestra el patrón "move in, move out" devolviendo una tupla
// para retornar el nuevo ownership y metadata del original.
fn transformar_con_metadata(texto: String) -> (String, usize) {
let longitud = texto.len();
let transformado = texto.to_uppercase();
(transformado, longitud) // Retornamos el nuevo String y el tamaño original
}
// Utiliza std::mem::replace para extraer un valor de una referencia mut
// y dejar un valor por defecto en su lugar.
fn extraer_con_reemplazo(contenedor: &mut String) -> String {
mem::replace(contenedor, String::from("VACÍO"))
}
// Muestra la consistencia de ramas: el valor debe ser devuelto
// o movido en todos los caminos posibles.
fn bifurcacion_segura(condicion: bool, s: String) -> String {
if condicion {
// El ownership de s se mueve al retorno de la función
s
} else {
// El ownership de s se libera (drop) aquí, pero devolvemos un String nuevo
String::from("Valor por defecto")
}
}
fn main() {
// 1. Movimiento y retorno (Pattern: move in, move out)
let mi_texto = String::from("hola");
let (texto_final, tamano) = transformar_con_metadata(mi_texto);
// println!("{}", mi_texto); // ERROR: mi_texto ya no tiene ownership
println!("Texto: {}, Longitud original: {}", texto_final, tamano);
// 2. Intercambio de valor con referencias mutables
let mut buffer = String::from("Original");
let extraido = extraer_con_reemplazo(&mut buffer);
println!("Extraído: '{}', Buffer actual: '{}'", extraido, buffer);
// 3. Consistencia en ramas de control
let dato = String::from("Consistente");
let resultado = bifurcacion_segura(true, dato);
println!("Resultado final: {}", resultado);
}
Análisis de Código
transformar_con_metadata(texto: String) -> (String, usize): Esta función toma la propiedad detexto(tipoString). Devuelve una tupla que contiene un nuevoString(el ownership del texto transformado) y unusize(la longitud original). Enmain, la variablemi_textoqueda invalidada después de esta llamada.extraer_con_reemplazo(contenedor: &mut String) -> String: Recibe una referencia mutablecontenedor. Mediantemem::replace, extraemos el contenido original decontenedorpara devolverlo y colocamosString::from("VACÍO")en su lugar. Esto permite “sacar” el valor sin perder la propiedad de la variablebufferenmain.bifurcacion_segura(condicion: bool, s: String) -> String: Esta función garantiza la consistencia de propiedad. En el bloqueif,sse mueve al valor de retorno. En el bloqueelse,sse destruye al finalizar el scope, pero la función siempre devuelve unString. Esto permite queresultadoenmainsea siempre válido, independientemente de la rama ejecutada.- Variables en
main:mi_texto: UnStringcuyo ownership se transfiere atransformar_con_metadata.texto_final: Recibe el ownership del string transformado mediante la tupla.buffer: UnStringmutante cuya propiedad se mantiene, pero cuyo contenido es alterado porextraer_con_reemplazo.extraido: Contiene el valor que anteriormente pertenecía abuffer.dato: UnStringcuyo ownership entra en la lógica de decisión debifurcacion_segura.
N° 26