Conversión de bases y manejo de errores en Rust

Descripción del problema

Se requiere implementar una función que convierta un número representado como un slice de dígitos de una base origen a una base destino, devolviendo un Result con el vector de dígitos resultante o un error. La firma de la función es:

#[derive(Debug, PartialEq)]
pub enum ConversionError {
    InvalidSourceBase,
    InvalidDestinationBase,
    InvalidDigit(u32),
}

pub fn convertir(digitos: &[u32], base_origen: u32, base_destino: u32) -> Result<Vec<u32>, ConversionError> {
    unimplemented!()
}

La función debe manejar casos especiales como slices vacíos (equivalentes a 0) y eliminar dígitos cero a la izquierda en la salida, excepto cuando el número es cero.

Principios de conversión

La conversión entre bases generalmente implica dos pasos: primero, convertir el número de la base origen a decimal, y luego convertir ese valor decimal a la base destino. Por ejemplo, el número binario [1, 0, 1, 0] (base 2) equivale a 10 en decimal, y luego se puede convertir a hexadecimal.

Implementación paso a paso

1. Validación de entradas

Es esencial verificar que las bases sean válidas (mayores o iguales a 2) y que cada dígito sea menor que la base origen. Si no se cumple, se deuvelve el error correspondiente.

fn validar_parametros(digitos: &[u32], base_origen: u32, base_destino: u32) -> Result<(), ConversionError> {
    if base_origen < 2 {
        return Err(ConversionError::InvalidSourceBase);
    }
    if base_destino < 2 {
        return Err(ConversionError::InvalidDestinationBase);
    }
    for &digito in digitos {
        if digito >= base_origen {
            return Err(ConversionError::InvalidDigit(digito));
        }
    }
    Ok(())
}

2. Manejo de casos especiales

Un slice vacío o uno donde todos los dígitos son cero debe devolverse como [0]. Esto simplifica la lógica posterior.

fn es_cero(digitos: &[u32]) -> bool {
    digitos.is_empty() || digitos.iter().all(|&d| d == 0)
}

3. Algoritmo de conversión

La implementación completa integra validación y conversión. Se utiliza checked_mul y checked_add para evitar desbordamientos aritméticos.

pub fn convertir(digitos: &[u32], base_origen: u32, base_destino: u32) -> Result<Vec<u32>, ConversionError> {
    validar_parametros(digitos, base_origen, base_destino)?;
    
    if es_cero(digitos) {
        return Ok(vec![0]);
    }
    
    // Paso 1: convertir a decimal
    let mut valor_decimal = 0u32;
    for &digito in digitos {
        valor_decimal = valor_decimal
            .checked_mul(base_origen)
            .ok_or(ConversionError::InvalidDigit(digito))?
            .checked_add(digito)
            .ok_or(ConversionError::InvalidDigit(digito))?;
    }
    
    // Paso 2: convertir a la base destino
    let mut resultado = Vec::new();
    let mut residuo = valor_decimal;
    while residuo > 0 {
        resultado.push(residuo % base_destino);
        residuo /= base_destino;
    }
    resultado.reverse(); // los dígitos se generan en orden inverso
    
    Ok(resultado)
}

Análisis de casos de prueba

Los tests verifican diversas situaciones: conversiones simples, manejo de ceros, dígitos inválidos y bases incorrectas. Por ejemplo, convertir [1, 1, 2, 0] de base 3 a base 16 da como resultado [2, 10], donde 10 representa 'A' en hexadecimal. Un slice vacío siempre produce [0], y los dígitos como 2 en base 2 generan un error.

Importancia del manejo de errores

El uso de un enum para errores permite un manejo explícito y seguro. Cada variante (InvalidSourceBase, InvalidDestinationBase, InvalidDigit) identifica claramente el problema, facilitando la depuración y el tratamiento adecuado en el código que llama a la función.

Consideraciones de seguridad y eficiencia

Al trabajar con números grandes, se deben prevenir desbordamientos usando métodos como checked_mul. La complejidad temporal es O(n log m), donde n es el número de dígitos y m el valor numérico. Para optimizar, se puede preasignar memoria en el vecter resultado según la estimación de dígitos necesarios.

Esta práctica fortalece habilidades esenciales en Rust, como la manipulación de slices, el manejo de errores con Result, y la implementación de algoritmos numéricos robustos, aplicables en áreas como criptografía y procesamiento de datos.

Etiquetas: Rust base-conversion error-handling algorithms numeric-representation

Publicado el 6-6 22:55