No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

No se trata de lo que quieres comprar, sino de quién quieres ser. Aprovecha el precio especial.

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

15 Días
11 Hrs
15 Min
55 Seg

Creando nuestra calculadora

13/20
Recursos

Es momento de desarrollar tu primera gran aplicación en Rust. Eres libre de idear y programar una aplicación de consola como una calculadora o un calendario. Para esto, ten presenta algunos consejos:

Utilización de dependencias en Rust

Explora la gran cantidad de dependencias que tiene Rust en su repositorio para encontrar las que necesites para tu app.

Algunas dependencias interesantes:

Recuerda agregar la dependencia en el archivo Cargo.toml para su posterior descarga e importarla con use.

use regex::Regex;

fn main() {
    // ...
}

Cada dependencia tiene su propia documentación para que sepas cómo utilizarla.

Conversiones de datos en Rust

Veamos algunas formas de convertir datos en Rust que te servirá de ahora en adelante.

String a entero

Hay varias formas de declarar una variable del tipo String:

let nombre = "123".to_string();

let nombre: String = "123".to_string();

let nombre = String::from("123");

let mut nombre: String = String::new();
nombre = "123".to_string();

Conviértelo a número entero con parse(), pero teniendo en cuenta de borrar los espacios en blanco con trim() para evitar inconvenientes y capturar los errores con unwrap().

let number: i32 = nombre.trim().parse().unwrap();
println!("{}", number+1);

String vs. &str

Recuerda que el tipo de dato String permite manipular una cadena de texto, mientras que el tipo de dato &str contiene la referencia a un String, pero solo contiene su valor.
Cuando le asignas a una variable un valor con dobles comillas "", las mismas crean un &str, por esto utilizamos to_string() para que retorne el tipo de dato en formato String y poder manipularlo. Y por este mismo motivo necesitas un String para hacer un .trim().parse() y convertir la variable al tipo entero.

Para convertir un String a &str, solo agrega el & (Ampersand) delante del nombre de la variable o con as_str().

let nombre: String = "123".to_string();
let nombre_plano: &str = &nombre;

let nombre: String = "123".to_string();
let nombre_plano: &str = nombre.as_str();

Muchas formas de hacer lo mismo ¿No te parece?

TIP: Por convencion, en Rust, todas las variables y nombres de funciones que declares utilizan snackcase, o sea, un _ para separar las palabras. EJ: my_variable o my_function.

 
Rust posee una curva de aprendizaje algo más compleja cuando se trata de tipos de datos y conversiones. Como siempre, la práctica hará que puedas comprender cuándo utilizar cada tipo y ser más veloz resolviendo problemas.


Contribución creada por: Kevin Fiorentino.

Aportes 38

Preguntas 9

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Regex

(\d+)\s?\+\s?(\d+)

Esta es mi solucion, con una funcion para que no se repita tanto el codigo:

use regex::Regex;

fn make_operation(reg: Regex, mut expresion: String, operation: &str) -> String {
    if operation.is_empty() {
        return "".to_string();
    }
    loop {
        //Aplicar operaciones
        let caps = reg.captures(expresion.as_str());

        if caps.is_none() {
            break;
        }
        let caps = caps.unwrap();

        let cap_expresion = caps.get(0).unwrap().as_str();
        let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();

        let result = match operation {
            "+" => left_value + right_value,
            "-" => left_value - right_value,
            "*" => left_value * right_value,
            "/" => left_value / right_value,
            _ => 0,
        };

        expresion = expresion.replace(cap_expresion, &result.to_string());
    }
    expresion
}

fn main() {
    //Regex
    let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
    let re_less = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();
    let re_mult = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
    let re_div = Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();

    //Traer datos del usuario
    println!("Por favor introduce tu expresion");
    let mut expresion = String::new();
    std::io::stdin().read_line(&mut expresion).unwrap();

    //Multiplicacion
    expresion = make_operation(re_mult, expresion, "*");
    //Division
    expresion = make_operation(re_div, expresion, "/");
    //Suma
    expresion = make_operation(re_add, expresion, "+");
    //Resta
    expresion = make_operation(re_less, expresion, "-");

    //Mostrar resultados
    println!("Resultados: {}", expresion);
}

regex que incluye todas las operaciones aritméticas

(\d+)\s+?(\+|\*|/|\-)\s+?(\d+)

calculadora científica ??

Mi código repo le agregué enums para controlar de mejor forma las operaciones.

Aqui dejo mi pequeño aporte:

use regex::Regex;

fn main() {
    println!("Calculadora");

    // Regex
    let re_add = Regex::new(r"(\d+)\s?([\+\-])\s?(\d+)").unwrap();
    let re_mult = Regex::new(r"(\d+)\s?([/\*])\s?(\d+)").unwrap();

    // Traer datos del usuario
    println!("Ingrese una operacion: ");
    let mut expression = String::new();
    std::io::stdin().read_line(&mut expression).unwrap();

    // Multiplicacion y Division
    loop {
        let caps = re_mult.captures(expression.as_str());

        if caps.is_none() {
            break;
        }
        let caps = caps.unwrap();
        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value: i32 = caps.get(3).unwrap().as_str().parse().unwrap();
        let sign = caps.get(2).unwrap().as_str();
        
        if sign == "*" {
            let result = left_value * right_value;
            expression = expression.replace(cap_expression, &result.to_string());
        } else if sign == "/" {
            let result = left_value / right_value;
            expression = expression.replace(cap_expression, &result.to_string());
        }
    }

    // Suma y resta
    loop {
        let caps = re_add.captures(expression.as_str());

        if caps.is_none() {
            break;
        }
        let caps = caps.unwrap();
        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value: i32 = caps.get(3).unwrap().as_str().parse().unwrap();
        let sign = caps.get(2).unwrap().as_str();

        if sign == "+" {
            let result = left_value + right_value;
            expression = expression.replace(cap_expression, &result.to_string());
        } else if sign == "-" {
            let result = left_value - right_value;
            expression = expression.replace(cap_expression, &result.to_string());
        }
    }

    // Mostrar resultado
    println!("Resultado: {}", expression);
}

Quizá para este ejercicio en particular hubiese preferido usar parser combinators, pareciese que el crate nom es bastante popular en la comunidad Rust. Pero para seguir lo indicado en esta clase seguí usando expresiones regulares.

En la siguiente solución podrán apreciar un par de cosas:

  • Acceder a las capturas por medio de nombres, en vez de posición, esto es posible al usar expresiones regulares similares a: r"(?P<left>\d+)\s?\+\s?(?P<right>\d+)". Me parece que el código queda un poco más legible de esta manera, al menos, al momento de acceder a la captura en específico parece más claro.
  • Uso de funciones auxiliares, de modo que podamos luego desarrollar pruebas unitarias para verificar el correcto funcionamiento de dichas porciones de código.
  • Uso de tuplas en donde el primer elemento indica el operador y el segundo su expresión regular asociada.
  • Uso de fold para reducir la expresión original

Acá el módulo principal:

use regex::Regex;

fn main() {
    println!("expression: ");
    let mut expression = String::new();
    std::io::stdin().read_line(&mut expression).unwrap();

    println!("Result: {}", reduce_expression(expression))
}

fn reduce_expression(mut expression: String) -> String {
    let re_add = Regex::new(r"(?P<left>\d+)\s?\+\s?(?P<right>\d+)").unwrap();
    let re_sub = Regex::new(r"(?P<left>\d+)\s?\-\s?(?P<right>\d+)").unwrap();
    let re_mult = Regex::new(r"(?P<left>\d+)\s?\*\s?(?P<right>\d+)").unwrap();
    let re_div = Regex::new(r"(?P<left>\d+)\s?/\s?(?P<right>\d+)").unwrap();

    let precedence = [("/", re_div), ("*", re_mult), ("+", re_add), ("-", re_sub)];

    expression = precedence
        .into_iter()
        .fold(expression, |acc, (operator, re)| apply(re, acc, operator));

    expression
}

// apply the given operation
fn apply(re: Regex, mut expression: String, operator: &str) -> String {
    loop {
        let caps = re.captures(expression.as_str());

        if caps.is_none() {
            break;
        }

        let caps = caps.unwrap();
        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value: i32 = caps.name("left").unwrap().as_str().parse().unwrap();
        let right_value: i32 = caps.name("right").unwrap().as_str().parse().unwrap();
        let result = match operator {
            "+" => left_value + right_value,
            "-" => left_value - right_value,
            "*" => left_value * right_value,
            "/" => left_value / right_value,
            _ => 0,
        };

        expression = expression.replace(cap_expression, &result.to_string());
    }

    expression
}

Y sus correspondientes pruebas unitarias, en realidad hice una sola por aquello de la pereza.

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_apply() {
        let expression: String = "12/2*4+1*12-32".to_string();

        assert_eq!(reduce_expression(expression), "4");
    }
}

Pienso que un pequeño cambio en la regex podría aportar más flexibilidad al usuario: en lugar de utilizar el metachar \s? se puede utilizar \s* para que si el usuario ingresa 10 + 20 , igual tome los valores correspondientes

I think Platzi must clarify this exercise because the teacher’s implementation is wrong.

Aquí dejo mi aporte, todo esta estructurado en un main, en próximas sesiones lo hare mediante funciones. Le hice una prueba intensiva de escritorio. Les dejo el link del github.

Calculadora

Divison

let _re_div=Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();

les dejo mi codigo fuente,
github

use std::io; use regex::Regex; fn aplicar\_operacion\<F>(expression: \&mut String, re: \&Regex, operacion: F) where F: Fn(i32, i32) -> i32, { loop { let caps = re.captures(expression.as\_str()); if caps.is\_none() { break; } let caps = caps.unwrap(); let caps\_expression = caps.get(0).unwrap().as\_str(); let left\_value: i32 = caps.get(1).unwrap().as\_str().parse().unwrap(); let right\_value: i32 = caps.get(2).unwrap().as\_str().parse().unwrap(); let resultado = operacion(left\_value, right\_value); \*expression = expression.replace(caps\_expression, \&resultado.to\_string()); } } fn aplicar\_multiplicacion\_y\_division(expression: \&mut String) { let re\_mult\_div: Regex = Regex::*new*(r"(\d+)\s?(\[\*/])\s?(\d+)").unwrap(); loop { let caps = re\_mult\_div.captures(expression.as\_str()); if caps.is\_none() { break; } let caps = caps.unwrap(); let caps\_expression = caps.get(0).unwrap().as\_str(); let left\_value: i32 = caps.get(1).unwrap().as\_str().parse().unwrap(); let operator = caps.get(2).unwrap().as\_str(); let right\_value: i32 = caps.get(3).unwrap().as\_str().parse().unwrap(); let resultado = if operator == "\*" { left\_value \* right\_value } else { if right\_value != 0 { left\_value / right\_value } else { println!("Error: División por cero no permitida."); return; } }; \*expression = expression.replace(caps\_expression, \&resultado.to\_string()); } } fn main() { // Expresiones Regulares let re\_add: Regex = Regex::*new*(r"(\d+)\s?\\+\s?(\d+)").unwrap(); let re\_sub: Regex = Regex::*new*(r"(\d+)\s?\\-\s?(\d+)").unwrap(); // Traer datos del usuario println!("Por favor introduce tu expresion: "); let mut expression = String::*new*(); io::stdin().read\_line(\&mut expression).unwrap(); // 1. Resolver multiplicaciones y divisiones primero, de izquierda a derecha aplicar\_multiplicacion\_y\_division(\&mut expression); // 2. Resolver restas y sumas después aplicar\_operacion(\&mut expression, \&re\_sub, |a, b| a - b); aplicar\_operacion(\&mut expression, \&re\_add, |a, b| a + b); // Mostrar Resultado println!("Resultado: {}", expression); }
Mi solucion fue crear una funcion para no repetir codigo use regex::Regex; fn main() { // Regex // (\d+) \s? \\+ \s? (\d+) -> ej: 1 + 14 let re\_add = Regex::new(r"(\d+)\s?\\+\s?(\d+)").unwrap(); let re\_less = Regex::new(r"(\d+)\s?\\-\s?(\d+)").unwrap(); let re\_mult = Regex::new(r"(\d+)\s?\\\*\s?(\d+)").unwrap(); let re\_div: Regex = Regex::new(r"(\d+)\s?\\/\s?(\d+)").unwrap(); // Traer datos del usuario println!("Ingrese la operación a realizar: "); let mut <u>expression</u> = String::new(); std::io::stdin().read\_line(\&mut <u>expression</u>).unwrap(); //Mupltiplicacion <u>expression</u> = operation(re\_mult, <u>expression</u>, "\*"); //Division <u>expression</u> = operation(re\_div, <u>expression</u>, "/"); //Resta <u>expression</u> = operation(re\_less, <u>expression</u>, "-"); //Suma <u>expression</u> = operation(re\_add, <u>expression</u>, "+"); // Mostrar datos println!("La expresión es: {}", <u>expression</u>); // Mostrar error} pub fn operation(reg: Regex, mut <u>expresion</u>: String, operation: \&str) -> String { if operation.is\_empty() { return "".to\_string(); } loop { // Aplicar Operaciones let cap = reg.captures(<u>expresion</u>.as\_str()); if cap.is\_none() { break; } let cap = cap.unwrap(); let cap\_expression = cap.get(0).unwrap().as\_str(); let left\_value: i32 = cap.get(1).unwrap().as\_str().parse().unwrap(); let right\_value: i32 = cap.get(2).unwrap().as\_str().parse().unwrap(); let result = match operation { "+" => left\_value + right\_value, "-" => left\_value - right\_value, "\*" => left\_value \* right\_value, "/" => left\_value / right\_value, \_ => 0, }; <u>expresion</u> = <u>expresion</u>.replace(cap\_expression, result.to\_string().as\_str()); } return <u>expresion</u>;}
Así resolví el reto de la calculadora. Usé un concepto de programación funcional, que consiste en pasar funciones como argumentos de otras funciones, para evitar repetir código. Aquí va: ```js use regex::Regex; fn multiply(x: i32, y: i32) -> i32 { return x * y; } fn divide(x: i32, y: i32) -> i32 { return x / y; } fn add(x: i32, y: i32) -> i32 { return x + y; } fn subtract(x: i32, y: i32) -> i32 { return x - y; } fn evaluate_expression<F>(expression: String, regex_expression: Regex, operation: F) -> String where F: Fn(i32, i32) -> i32 { let mut evaluated_expression: String = expression; loop { /* Capturo la expresión */ let captures: Option<regex::Captures<'_>> = regex_expression.captures(evaluated_expression.as_str()); /* A continuación, se va a validar si la captura contiene un None. Si es así, es porque no hay más operaciones, y sale del bucle. Si no, vuelve a evaluar la cadena, para buscar operadores y operandos y resolver la operación. */ if captures.is_none() { return evaluated_expression; } let captures = captures.unwrap(); let captured_expression = captures.get(0).unwrap().as_str(); let left_value: i32 = captures.get(1).unwrap().as_str().parse().unwrap(); let right_value: i32 = captures.get(2).unwrap().as_str().parse().unwrap(); let operated_value: i32 = operation(left_value, right_value); evaluated_expression = evaluated_expression.replace(captured_expression, &operated_value.to_string()); } } fn main() { println!("Bienvenidx a la calculadora científica."); /* Evaluar una expresión mediante regex. La expresión para la suma sería: (\d+) \s? \+ \s? (\d+)*/ let regex_multiply = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap(); let regex_divide = Regex::new(r"(\d+)\s?\/\s?(\d+)").unwrap(); let regex_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap(); let regex_subtract = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap(); /* Traer los datos del usuario. */ println!("Por favor, introduce tu expresión:"); let mut expression = String::new(); std::io::stdin().read_line(&mut expression).unwrap(); /* Evaluar las expresiones */ let mut result: String = evaluate_expression(expression, regex_multiply, multiply); println!("{}", result); result = evaluate_expression(result, regex_divide, divide); println!("{}", result); result = evaluate_expression(result, regex_add, add); println!("{}", result); result = evaluate_expression(result, regex_subtract, subtract); /* Mostrar el resultado */ println!("El resultado es: {}", result) } ```
Después de investigar (y experimentar) bastante, aquí está mi solución:use regex::Regex; fn operation(op\_type: \&str, mut <u>expression</u>: String, re: Regex) -> String {    loop {        let caps = re.captures(<u>expression</u>.as\_str());                if caps.is\_none() {            break;        }                let caps = caps.unwrap();                let cap\_expression: \&str = caps.get(0).unwrap().as\_str();        let mut <u>left\_value</u>: i32 = 0;        let right\_value: i32;        let mut <u>right\_value\_u32</u>: u32 = 0;                if op\_type != "sqrt" {            <u>left\_value</u> = caps.get(1).unwrap().as\_str().parse().unwrap();            right\_value = caps.get(2).unwrap().as\_str().parse().unwrap();            <u>right\_value\_u32</u> = caps.get(2).unwrap().as\_str().parse().unwrap();        } else {            right\_value = caps.get(1).unwrap().as\_str().parse().unwrap();        }         let mut <u>res</u>: i32 = 0;        if op\_type == "-" {            <u>res</u> = <u>left\_value</u> - right\_value;        } else if op\_type == "+" {            <u>res</u> = <u>left\_value</u> + right\_value;        } else if op\_type == "/" {            <u>res</u> = <u>left\_value</u> / right\_value;        } else if op\_type == "\*" {            <u>res</u> = <u>left\_value</u> \* right\_value;        } else if op\_type == "^" {            let res\_option = <u>left\_value</u>.checked\_pow(<u>right\_value\_u32</u>);            if res\_option.is\_none() {                println!("El número es demasiado grande");                break;            } else {                <u>res</u> = res\_option.unwrap();            }        } else if op\_type == "sqrt" {            <u>res</u> = (right\_value as f32).sqrt() as i32;        }         <u>expression</u> = <u>expression</u>.replace(cap\_expression, &<u>res</u>.to\_string());     }    return <u>expression</u>;} fn main() {    // Regex     let re\_sqrt: Regex = Regex::new(r"sqrt(\d+(\\.\d+)?)").unwrap();    let re\_exp: Regex = Regex::new(r"(\d+)\s?\\^\s?(\d+)").unwrap();    let re\_div: Regex = Regex::new(r"(\d+)\s?\\/\s?(\d+)").unwrap();    let re\_mult: Regex = Regex::new(r"(\d+)\s?\\\*\s?(\d+)").unwrap();    let re\_sub: Regex = Regex::new(r"(\d+)\s?\\-\s?(\d+)").unwrap();    let re\_add: Regex = Regex::new(r"(\d+)\s?\\+\s?(\d+)").unwrap();     // User input     println!("Por favor ingrese su expresión: ");    let mut <u>expression</u>: String = String::new();    std::io::stdin().read\_line(\&mut <u>expression</u>).unwrap();     // Operations    <u>expression</u> = operation("sqrt", <u>expression</u>, re\_sqrt);    <u>expression</u> = operation("^", <u>expression</u>, re\_exp);    <u>expression</u> = operation("/", <u>expression</u>, re\_div);    <u>expression</u> = operation("\*", <u>expression</u>, re\_mult);    <u>expression</u> = operation("-", <u>expression</u>, re\_sub);    <u>expression</u> = operation("+", <u>expression</u>, re\_add);        // Output the results    println!("Resultado: {}", <u>expression</u>);} ```js use regex::Regex; fn operation(op_type: &str, mut expression: String, re: Regex) -> String { loop { let caps = re.captures(expression.as_str()); if caps.is_none() { break; } let caps = caps.unwrap(); let cap_expression: &str = caps.get(0).unwrap().as_str(); let mut left_value: i32 = 0; let right_value: i32; let mut right_value_u32: u32 = 0; if op_type != "sqrt" { left_value = caps.get(1).unwrap().as_str().parse().unwrap(); right_value = caps.get(2).unwrap().as_str().parse().unwrap(); right_value_u32 = caps.get(2).unwrap().as_str().parse().unwrap(); } else { right_value = caps.get(1).unwrap().as_str().parse().unwrap(); } let mut res: i32 = 0; if op_type == "-" { res = left_value - right_value; } else if op_type == "+" { res = left_value + right_value; } else if op_type == "/" { res = left_value / right_value; } else if op_type == "*" { res = left_value * right_value; } else if op_type == "^" { let res_option = left_value.checked_pow(right_value_u32); if res_option.is_none() { println!("El número es demasiado grande"); break; } else { res = res_option.unwrap(); } } else if op_type == "sqrt" { res = (right_value as f32).sqrt() as i32; } expression = expression.replace(cap_expression, &res.to_string()); } return expression; } fn main() { // Regex let re_sqrt: Regex = Regex::new(r"sqrt(\d+(\.\d+)?)").unwrap(); let re_exp: Regex = Regex::new(r"(\d+)\s?\^\s?(\d+)").unwrap(); let re_div: Regex = Regex::new(r"(\d+)\s?\/\s?(\d+)").unwrap(); let re_mult: Regex = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap(); let re_sub: Regex = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap(); let re_add: Regex = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap(); // User input println!("Por favor ingrese su expresión: "); let mut expression: String = String::new(); std::io::stdin().read_line(&mut expression).unwrap(); // Operations expression = operation("sqrt", expression, re_sqrt); expression = operation("^", expression, re_exp); expression = operation("/", expression, re_div); expression = operation("*", expression, re_mult); expression = operation("-", expression, re_sub); expression = operation("+", expression, re_add); // Output the results println!("Resultado: {}", expression); } ```
```python use regex::Regex; fn main() { println!("Hola Platzi"); let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap(); let re_sub = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap(); let re_mult = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap(); let re_div = Regex::new(r"(\d+)\s?\/\s?(\d+)").unwrap(); println!("Por favor introduce tu expresion: "); let mut expression: String = String::new(); std::io::stdin().read_line(&mut expression).unwrap(); loop { let caps = re_mult.captures(expression.as_str()); if caps.is_none() { break; } let caps = caps.unwrap(); let cap_expression = caps.get(0).unwrap().as_str(); let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap(); let rigth_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap(); let mult = left_value * rigth_value; expression = expression.replace(cap_expression, &mult.to_string()); } loop { let caps = re_div.captures(expression.as_str()); if caps.is_none() { break; } let caps = caps.unwrap(); let cap_expression = caps.get(0).unwrap().as_str(); let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap(); let rigth_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap(); let div = left_value / rigth_value; expression = expression.replace(cap_expression, &div.to_string()); } loop { let caps = re_add.captures(expression.as_str()); if caps.is_none() { break; } let caps = caps.unwrap(); let cap_expression = caps.get(0).unwrap().as_str(); let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap(); let rigth_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap(); let addition = left_value + rigth_value; expression = expression.replace(cap_expression, &addition.to_string()); } loop { let caps = re_sub.captures(expression.as_str()); if caps.is_none() { break; } let caps = caps.unwrap(); let cap_expression = caps.get(0).unwrap().as_str(); let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap(); let rigth_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap(); let subtraction = left_value - rigth_value; expression = expression.replace(cap_expression, &subtraction.to_string()); } println!("Resultado: {}", expression) } ```

agrupé multiplicacion y division porque sino la que iria antes en el código es la que tendria preferencia en vez de ir en orden de lectura (propiedad asociativa)

creo que agrupar suma y resta no seria necesario porque son conmutativas

use regex::Regex;

fn main() {
    let re_add_sub = Regex::new("(\\d+)\\s?([\\+\\-])\\s?(\\d+)").unwrap();
    let re_mult_div = Regex::new("(\\d+)\\s?([\\*\\/])\\s?(\\d+)").unwrap();

    println!("Introduzca la operación: ");
    let mut expression = String::new();
    std::io::stdin().read_line(&mut expression).unwrap();
    //multiplication and division
    loop{
        let caps = re_mult_div.captures(&expression);
        if caps.is_none(){
            break;
        }

        let caps = caps.unwrap();

        // let caps_expression = caps.get(0).unwrap().as_str();
        let caps_num1 : i32 = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
        let caps_num2 : i32 = caps.get(3).unwrap().as_str().parse::<i32>().unwrap();
        let operator = &caps[2];
        let result = match operator {
            "*" => caps_num1 * caps_num2,
            "/" => caps_num1 / caps_num2,
            _ => {
                println!("Operador no válido: {}", operator);
                break;
            }
        };
        expression = expression.replacen(&caps[0], &result.to_string(), 1);
        println!("La suma es: {}", result);
    }

    //add and sub
    loop{
        let caps = re_add_sub.captures(&expression);
        if caps.is_none(){
            break;
        }

        let caps = caps.unwrap();

        // let caps_expression = caps.get(0).unwrap().as_str();
        let caps_num1 : i32 = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
        let caps_num2 : i32 = caps.get(3).unwrap().as_str().parse::<i32>().unwrap();
        let operator = &caps[2];
        let result = match operator {
            "+" => caps_num1 + caps_num2,
            "-" => caps_num1 - caps_num2,
            _ => {
                println!("Operador no válido: {}", operator);
                break;
            }
        };
        expression = expression.replacen(&caps[0], &result.to_string(), 1);
        println!("La suma es: {}", result);
    }
}

Mi Solución cambie la expresión regular por 2 la primera evalua multiplicación y división y la segunda suma y resta.

Luego saca el simbolo para saber que operación realizar.

use regex::Regex;
use std::io::stdin;

fn operaciones(re: Regex, mut expression: String) -> String {
    loop {
        //multipliction
        let caps = re.captures(expression.as_str());
        if caps.is_none() {
            break;
        }
        let caps = caps.unwrap();
        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let symbol: &str = caps.get(2).unwrap().as_str();
        let right_value: i32 = caps.get(3).unwrap().as_str().parse().unwrap();
        let operation = match symbol {
            "*" => left_value * right_value,
            "/" => left_value / right_value,
            "-" => left_value - right_value,
            "+" => left_value + right_value,
            &_ => todo!(),
        };
        expression = expression.replace(cap_expression, &operation.to_string());
    }
    return expression;
}

fn main() {
    //Regex

    let re_expresion_first = Regex::new(r"(\d+)\s?(\*|\/)\s?(\d+)").unwrap();
    let re_expresion_second = Regex::new(r"(\d+)\s?(\+|\-)\s?(\d+)").unwrap();

    // traer datos de usuario
    println!("Por favor introduce tu expresión: ");
    let mut expression = String::new();
    stdin().read_line(&mut expression).unwrap();

    //aplicar operaciones
    expression = operaciones(re_expresion_first, expression);
    expression = operaciones(re_expresion_second, expression);

    // mostrar resultados
    println!("Resultado: {}", expression);
}

Muy buena la clase, lo que si puedo recomendar es que deben explicar de manera mas detallada para los principiantes que el loop simpre va a iterar sobre las dos primeros operandos de la variable expression. Es decir, si yo como usuario introduzco A+B+C la primera vez que itere el programa este me va a reconocer solo A+B, luego reemplaza A+B por el resultado de esta suma en la variable expression. En la segunda iteracion tenemos que expression = resultadoA+B + C, y esta seria la ultima iteracion del programa ya que en expression no conseguiria mas regex de la forma D + D.

Pero me parece que tiene sus fallas

use regex::Regex;

fn main() {
    println!("Hola Platzi");

    // Regex
    let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
    let re_mult = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
    let re_resta = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();
    let re_div = Regex::new(r"(\d+)\s?\/\s?(\d+)").unwrap();

    // Traer datos del usuario
    println!("Por favor introduce tu expresion: ");
    let mut expression = String::new();
    std::io::stdin().read_line(&mut expression).unwrap();

    // multiplicacion
    loop {
        let caps = re_mult.captures(expression.as_str());

        if caps.is_none() {
            break;
        }

        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value : i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value : i32 = caps.get(2).unwrap().as_str().parse().unwrap();

        let mult = left_value * right_value;

        expression = expression.replace(cap_expression, &mult.to_string());

    }

    //suma
    loop {
        let caps = re_add.captures(expression.as_str());

        if caps.is_none() {
            break;
        }

        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value : i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value : i32 = caps.get(2).unwrap().as_str().parse().unwrap();

        let addition = left_value + right_value;

        expression = expression.replace(cap_expression, &addition.to_string());

    }

        // división
        loop {
            let caps = re_div.captures(expression.as_str());
    
            if caps.is_none() {
                break;
            }
    
            let caps = caps.unwrap();
    
            let cap_expression = caps.get(0).unwrap().as_str();
            let left_value : i32 = caps.get(1).unwrap().as_str().parse().unwrap();
            let right_value : i32 = caps.get(2).unwrap().as_str().parse().unwrap();
    
            let div = left_value / right_value;
    
            expression = expression.replace(cap_expression, &div.to_string());
    
        }
    
        //resta
        loop {
            let caps = re_resta.captures(expression.as_str());
    
            if caps.is_none() {
                break;
            }
    
            let caps = caps.unwrap();
    
            let cap_expression = caps.get(0).unwrap().as_str();
            let left_value : i32 = caps.get(1).unwrap().as_str().parse().unwrap();
            let right_value : i32 = caps.get(2).unwrap().as_str().parse().unwrap();
    
            let resta = left_value - right_value;
    
            expression = expression.replace(cap_expression, &resta.to_string());
    
        }
    

    println!("Resultado: {}", expression);

}

Mi solución, pero espero mejorarla con las funciones.

use::regex::Regex;

fn main() {
    // Regex for matching the input
    let re_div = Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();
    let re_mul = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
    let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
    let re_sub = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();

    // User input
    println!("Enter an operation to perform: ");

    let mut expression = String::new();
    std::io::stdin().read_line(&mut expression).unwrap();

    // Division 
    loop {
        // Calculate operations
        let caps = re_div.captures(&expression);

        if caps.is_none() {
            break;
        }

        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
        let right_value = caps.get(2).unwrap().as_str().parse::<i32>().unwrap();

        let result = left_value / right_value;

        expression = expression.replace(cap_expression, &result.to_string());
    }
    

    // Multiplication
    loop {
        // Calculate operations
        let caps = re_mul.captures(&expression);

        if caps.is_none() {
            break;
        }

        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
        let right_value = caps.get(2).unwrap().as_str().parse::<i32>().unwrap();

        let result = left_value * right_value;

        expression = expression.replace(cap_expression, &result.to_string());
    }

    // Addition
    loop {
        // Calculate operations
        let caps = re_add.captures(&expression);

        if caps.is_none() {
            break;
        }

        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
        let right_value = caps.get(2).unwrap().as_str().parse::<i32>().unwrap();

        let result = left_value + right_value;

        expression = expression.replace(cap_expression, &result.to_string());
    }

    // Subtraction
    loop {
        // Calculate operations
        let caps = re_sub.captures(&expression);

        if caps.is_none() {
            break;
        }

        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value = caps.get(1).unwrap().as_str().parse::<i32>().unwrap();
        let right_value = caps.get(2).unwrap().as_str().parse::<i32>().unwrap();

        let result = left_value - right_value;

        expression = expression.replace(cap_expression, &result.to_string());
    }

    println!("Result: {}", expression);
}```

esta clase me parecio bastante interesante y me hizo pensar en como funciona los lenguajes interpretados, por ejemplo python3. probablemente no funcionan asi pero pues imaginen crear un lenguaje de programacion en base a rust.

les comparto mi aporte:

use regex::Regex;

fn main() {
    // regex
    let re_add: Regex = Regex::new(r"(\d+)\s?([+-])\s?(\d+)").unwrap();
    let re_mul: Regex = Regex::new(r"(\d+)\s?([*/])\s?(\d+)").unwrap();

    // user input
    let mut expression = String::new();
    println!("Enter an expression: ");
    std::io::stdin().read_line(&mut expression).unwrap();

    // validate input
    if !re_add.is_match(&expression) && !re_mul.is_match(&expression) {
        println!("Invalid expression");
        return;
    }

    // multiplication and divition
    loop {
        let captures = re_mul.captures(expression.as_str());
        if captures.is_none() {
            break;
        }
        let captures = captures.unwrap();
        let cap_expression = captures.get(0).unwrap().as_str();
        let left_value = captures.get(1).unwrap().as_str().parse::<i32>().unwrap();
        let operator = captures.get(2).unwrap().as_str();
        let right_value = captures.get(3).unwrap().as_str().parse::<i32>().unwrap();

        let mut result_mul = 0;
        if operator == "*" {
            result_mul = left_value * right_value;
        } else if operator == "/" {
            result_mul = left_value / right_value;
        }

        expression = expression.replace(cap_expression, &result_mul.to_string());
    }

    // addition and subtraction
    loop {
        let captures = re_add.captures(expression.as_str());
        if captures.is_none() {
            break;
        }
        let captures = captures.unwrap();
        let cap_expression = captures.get(0).unwrap().as_str();
        let left_value = captures.get(1).unwrap().as_str().parse::<i32>().unwrap();
        let operator = captures.get(2).unwrap().as_str();
        let right_value = captures.get(3).unwrap().as_str().parse::<i32>().unwrap();

        let mut result_sum = 0;
        if operator == "+" {
            result_sum = left_value + right_value;
        } else if operator == "-" {
            result_sum = left_value - right_value;
        }

        expression = expression.replace(cap_expression, &result_sum.to_string());
    }

    // print result
    println!("Result: {}", expression);
}

Para tener en cuenta los números negativos en las suma u otra operación al inicio de la operación se debe agregar -?
ejemplo (-?\d+)\s?+\s?(\d+)

code

Usando match y una función para evitar repetir código

use regex::{Captures, Regex};

fn main() {
    // Traer datos del usuario
    println!("Por favor introduce tu expresion: ");
    let mut expresion = String::new();
    std::io::stdin().read_line(&mut expresion).unwrap();

    expresion = make_math(expresion, "*".to_string());
    expresion = make_math(expresion, "/".to_string());
    expresion = make_math(expresion, "+".to_string());
    expresion = make_math(expresion, "-".to_string());

    // Mostrar resultado
    println!("Resultado: {}", expresion)
}

fn make_math(expresion: String, operator: String) -> String {
    // Regex
    let re_mul = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
    let re_div = Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();
    let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
    let re_minus = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();

    let mut expresion = expresion;
    loop {
        // Aplicar operaciones
        let caps = match operator.as_str() {
            "*" => re_mul.captures(expresion.as_str()),
            "/" => re_div.captures(expresion.as_str()),
            "+" => re_add.captures(expresion.as_str()),
            "-" => re_minus.captures(expresion.as_str()),
            _ => panic!(),
        };

        // let caps = caps.captures(expresion.as_str());
        if caps.is_none() {
            break;
        }

        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();

        let result = match operator.as_str() {
            "*" => left_value * right_value,
            "/" => left_value / right_value,
            "+" => left_value + right_value,
            "-" => left_value - right_value,
            _ => panic!(),
        };

        expresion = expresion.replace(cap_expression, &(result).to_string());
    }

    return expresion;
}

Llevo mucho tiempo programando en Python y decidí que mi primer lenguaje, para aprender, bajo nivel iba a ser Rust. No ha sido fácil entender todo y menos tener tratar con los tipos y cosas parecidas al operador &. Pero, aquí está mi ejercicio. Traté de expandirlo un poco más y adicional a las 4 operaciones básicas, también soporta potenciación y agrupaciones con paréntesis.

use regex::Regex;

fn math_op(expression: &Regex, mut operation: String, operator: &str) -> String {

    loop {
        let caps = expression.captures(operation.as_str());

        if caps.is_none() {
            break;
        }

        let caps = caps.unwrap();
        let cap_expression = caps.get(0).unwrap().as_str();
        let cap_num_1: f64 = caps.get(1).unwrap().as_str().parse().unwrap();
        let cap_num_2: f64 = caps.get(2).unwrap().as_str().parse().unwrap();

        let result = match operator {

            "+" => cap_num_1 + cap_num_2, 
            "-" => cap_num_1 - cap_num_2,
            "*" => cap_num_1 * cap_num_2,
            "/" => cap_num_1 / cap_num_2,
            "^" =>  if cap_num_2 < 0.0 {cap_num_1/cap_num_2} else {cap_num_1.powi(cap_num_2 as i32)},
            _ => 0.0,
        };

        operation = operation.replace(cap_expression, &result.to_string());

    }

    operation

}


fn main() {

    // Regex

    let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
    let re_sub = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();
    let re_mult = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
    let re_div = Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();
    let power = Regex::new(r"(\d+)\s?\^\s?(-?\d+)").unwrap();
    let parentesis = Regex::new(r"\(([^()]+)\)").unwrap();
   
 // Traer datos del usuario
    println!("Por favor introduce tu expresion: ");
    let mut expression = String::new();
    std::io::stdin().read_line(&mut expression).unwrap();

    loop {

        let cap_par = parentesis.captures(expression.as_str());
       
        if cap_par.is_none() {
            break;
        }  
        
        let cap_par = cap_par.unwrap();
        let capture = cap_par.get(0).unwrap().as_str();

        println!("Formula entre parentesis {}", capture);


        let mut res = math_op(&power,  String::from(capture).clone(), "^");
        res = math_op(&re_mult,String::from(res).clone(), "*");
        res = math_op(&re_div, String::from(res).clone(), "/");
        res = math_op(&re_add, String::from(res).clone(), "+");
        res = math_op(&re_sub, String::from(res).clone(), "-");

        res = res.replace("(", "");
        res = res.replace(")", "");

        println!("{:?}", res);

        expression = expression.replace(capture, &res.to_string());
        
    }

    expression = math_op(&power, expression.clone(), "^");
    expression = math_op(&re_mult, expression.clone(), "*");
    expression = math_op(&re_div, expression.clone(), "/");
    expression = math_op(&re_add, expression.clone(), "+");
    expression = math_op(&re_sub, expression.clone(), "-");
    
    println!("Resultado: {}", expression)
    // Aplicar Operaciones

}
 

Por acá dejo mi solución, aún no hago funciones aparte, espero esa parte del curso:

use regex::Regex;

fn main() {
    //regex
    let re_mul = Regex::new(r"(\d+)\s?\*\s?(\d+)").unwrap();
    let re_div = Regex::new(r"(\d+)\s?/\s?(\d+)").unwrap();
    let re_add = Regex::new(r"(\d+)\s?\+\s?(\d+)").unwrap();
    let re_sub = Regex::new(r"(\d+)\s?\-\s?(\d+)").unwrap();

    //get user input
    println!("Please, enter your math expression: ");
    let mut expression = String::new();
    std::io::stdin().read_line(&mut expression).unwrap();

    //multiplication
    loop {
        //validate math operations
        let caps = re_mul.captures(expression.as_str());

        if caps.is_none() {
            break;
        }
        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();

        let mul =  left_value * right_value;

        expression = expression.replace(cap_expression, &mul.to_string());
    }

    //division
    loop {
        //validate math operations
        let caps = re_div.captures(expression.as_str());

        if caps.is_none() {
            break;
        }
        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();

        let div =  left_value / right_value;

        expression = expression.replace(cap_expression, &div.to_string());
    }

    //addition
    loop {
        //validate math operations
        let caps = re_add.captures(expression.as_str());

        if caps.is_none() {
            break;
        }
        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();

        let addition =  left_value + right_value;

        expression = expression.replace(cap_expression, &addition.to_string());
    }

    //subtraction
    loop {
        //validate math operations
        let caps = re_sub.captures(expression.as_str());

        if caps.is_none() {
            break;
        }
        let caps = caps.unwrap();

        let cap_expression = caps.get(0).unwrap().as_str();
        let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value: i32 = caps.get(2).unwrap().as_str().parse().unwrap();

        let sub =  left_value - right_value;

        expression = expression.replace(cap_expression, &sub.to_string());
    }
    
    //print result
    println!("Result: {}", expression);
}

Hice la calculadora usando otras funciones para separar las responsabilidades, además, calcula resta, multiplicación y división. Incluso se puede usar parentesis, pero hay bugs porque falta aplicar la norma de operar de izquierda a derecha.
Posdata: En mi código se puede usar varios espacios porque al inicio los filtra.
Posdata: Hice uso de struct para estar tipando los objetos que estuve usando, solo aplique lo que se de Ts.
Este es el repositorio.

use std::io::{stdin};
use regex::{Regex, Captures};

struct RegexOperators {
    sum: Regex,
    substract: Regex,
    divide: Regex,
    multiply: Regex,
}

struct Expression {
    equation: String,
    parenthesis_1: String,
    parenthesis_2: String,
    variable_1: i16,
    variable_2: i16,
    symbol: String
}

pub fn exercise_calculator() {
    println!("Digit the equation.");

    let operators = RegexOperators {
        sum: Regex::new(r"(\(?)(\d+)(\+)(\d+)(\)?)").unwrap(), 
        substract: Regex::new(r"(\(?)(\d+)(\-)(\d+)(\)?)").unwrap(), 
        divide: Regex::new(r"(\(?)(\d+)(/)(\d+)(\)?)").unwrap(), 
        multiply: Regex::new(r"(\(?)(\d+)(\*)(\d+)(\)?)").unwrap(), 
    };
    
    let mut expression: String = String::new();
    stdin().read_line(&mut expression).unwrap();

    let expression_no_spaces: String = expression.replace(" ", "");

    let mut answer: String = calculator(expression_no_spaces, operators.divide);
    answer = calculator(answer, operators.multiply);
    answer = calculator(answer, operators.sum);
    answer = calculator(answer, operators.substract);

    println!("The answer is: {answer}");
}

fn destructure_caps (caps: Captures) -> Expression{
    let equation = Expression {
        parenthesis_1: caps.get(1).unwrap().as_str().to_string(),
        parenthesis_2: caps.get(5).unwrap().as_str().to_string(),
        symbol: caps.get(3).unwrap().as_str().to_string(),
        variable_1: caps.get(2).unwrap().as_str().parse().unwrap(),
        variable_2: caps.get(4).unwrap().as_str().parse().unwrap(),
        equation: caps.get(0).unwrap().as_str().to_string(),
    };

    return equation;
}

fn calculate (variable_1: i16, variable_2: i16, symbol: &str) -> i16{
    let result: i16 = match symbol {
        "+" => variable_1 + variable_2,
        "-" => variable_1 - variable_2,
        "*" => variable_1 * variable_2,
        "/" => if variable_2 == 0 {
            0
        } else {
            variable_1 / variable_2
        },
        _ => 0,
    };

    return result;
}

fn get_operation (equation: &Expression, result: i16) -> String {
    let new_operation: String;
    if equation.parenthesis_1 == "(" && equation.parenthesis_2 == ")"{
        new_operation = result.to_string();
    } else {
        new_operation = format!("{}{}{}", equation.parenthesis_1, &result.to_string(), equation.parenthesis_2);
    }
    return new_operation;
}

fn calculator (expression: String, operator: Regex) -> String {
    let mut operation: String = expression.clone();

    loop {
        let caps: Option<Captures> = operator.captures(&operation);
        if caps.is_none() {
            break;
        }
        let caps_unwraped: Captures = caps.unwrap();
        let equation: Expression = destructure_caps(caps_unwraped);
        let result: i16 = calculate(equation.variable_1, equation.variable_2, equation.symbol.as_str());
        let new_operation: String = get_operation(&equation, result);
        
        operation = operation.replace(equation.equation.as_str(), &new_operation);
    }

    return operation;
}

Esta es mi solucion para obtener los valores, pensando en un algo mas legible 😄

let caps = re_add.captures(&expresion).unwrap();
    let left_value : i32 = caps[1].parse().unwrap();
    let right_value : i32 = caps[2].parse().unwrap();
    println!("{:?} izq: {}, der: {}", caps, left_value, right_value);

Use una funcion para cumplir con el principio DRY (Don’t Repeat Yourself), usando ademas un switch (se llama match en Rust) y un enum para controlar el tipo de operacion a realizar

Ademas, aunque no se Regex aun, logre hacer una expresion que pudiera utilizar decimales, pues al haber divisiones es siempre posible que hayan resultados con decimales.

Creo que se puede mejorar el regex y se que posiblemente haya algun que otro error de logica, como en el orden de operaciones en el loop, asi que bienvenidas las recomendaciones en todo sentido

use regex::Regex;

enum TipoOperacion {
    SUMA,
    RESTA,
    MULTIPLICACION,
    DIVISION,
}

fn loop_operaciones(string_regex: Regex, mut expresion: String, tipo_operacion: TipoOperacion) -> String {
    loop {
        // Aplicar Operaciones
        let caps = string_regex.captures(expresion.as_str());

        if caps.is_none() {
            break;
        }

        let caps = caps.unwrap();

        let cap_expresion = caps.get(0).unwrap().as_str();

        // Aqui primero se accede a la posicion 1, se pasa a str, luego se parsea a f32 y un ultimo unwrap
        let left_value: f32 = caps.get(1).unwrap().as_str().parse().unwrap();
        let right_value: f32 = caps.get(2).unwrap().as_str().parse().unwrap();

        println!("{:?} izq: {} der: {}", caps, left_value, right_value); // :? dentro de los corchetes hace que de mas informacion
        println!("{}", expresion);
        let resultado: f32;

        match tipo_operacion {
            TipoOperacion::SUMA => resultado = left_value + right_value,
            TipoOperacion::RESTA => resultado = left_value - right_value,
            TipoOperacion::MULTIPLICACION => resultado = left_value * right_value,
            TipoOperacion::DIVISION => resultado = left_value / right_value,
        }

        expresion = expresion.replace(cap_expresion, &resultado.to_string()); // amperson al principio transforma un String a un str
    }
    return expresion;
}

fn main() {
    // Regex
    // Esta es la regex que me funciono para este caso y que desarrolle sobre la marcha, buscar mejor solucion
    let re_suma = Regex::new(r"(\d+\.?\d*)\s*\+\s*(\d+\.?\d*)").unwrap();
    let re_mult = Regex::new(r"(\d+\.?\d*)\s*\*\s*(\d+\.?\d*)").unwrap();
    let re_resta = Regex::new(r"(\d+\.?\d*)\s*\-\s*(\d+\.?\d*)").unwrap();
    let re_division = Regex::new(r"(\d+\.?\d*)\s*/\s*(\d+\.?\d*)").unwrap();

    // (\d+) \s? \+ \s? (\d+)
    /*
    Regex dividida en 5 partes
    Esta regex va a buscar primero un numero de uno o mas digitos
    Luego va a buscar un espacio que seria opcional
    Luego un signo de mas
    Luego otro espacio opcional
    Y por ultimo el otro numero
    Los espacios opcionales son por si el usuario ingresa por ejemplo 1 +2 o 2+ 34 o 3 + 6
    Y pues tambien sirve sin los espacios opcionales 2+5
    */

    // Traer datos del usuario
    let mut expresion = String::new();
    println!("Ingrese la expresion: ");
    std::io::stdin().read_line(&mut expresion).unwrap();
 
    expresion = loop_operaciones(re_mult, expresion, TipoOperacion::MULTIPLICACION);
    expresion = loop_operaciones(re_division, expresion, TipoOperacion::DIVISION);
    expresion = loop_operaciones(re_suma, expresion, TipoOperacion::SUMA);
    expresion = loop_operaciones(re_resta, expresion, TipoOperacion::RESTA);

    // Mostrar resultado
    println!("Resultado: {}", expresion);
}

Asi se desarrolla en la consola:

Ingrese la expresion: 
3*5/6+2-7+3*2+6/7
Captures({0: Some("3*5"), 1: Some("3"), 2: Some("5")}) izq: 3 der: 5
3*5/6+2-7+3*2+6/7

Captures({0: Some("3*2"), 1: Some("3"), 2: Some("2")}) izq: 3 der: 2
15/6+2-7+3*2+6/7

Captures({0: Some("15/6"), 1: Some("15"), 2: Some("6")}) izq: 15 der: 6
15/6+2-7+6+6/7

Captures({0: Some("6/7"), 1: Some("6"), 2: Some("7")}) izq: 6 der: 7
2.5+2-7+6+6/7

Captures({0: Some("2.5+2"), 1: Some("2.5"), 2: Some("2")}) izq: 2.5 der: 2
2.5+2-7+6+0.85714287

Captures({0: Some("7+6"), 1: Some("7"), 2: Some("6")}) izq: 7 der: 6
4.5-7+6+0.85714287

Captures({0: Some("13+0.85714287"), 1: Some("13"), 2: Some("0.85714287")}) izq: 13 der: 0.85714287
4.5-13+0.85714287

Captures({0: Some("4.5-13.857142"), 1: Some("4.5"), 2: Some("13.857142")}) izq: 4.5 der: 13.857142
4.5-13.857142

Resultado: -9.357142

técnicamente es una calculadora pero no cientifica 😛

Comparto mi solución:

<code> 
use regex::Regex;

fn main() {
  // Regex
  let re_add = Regex::new(r"(-?\d+)\s?([\+|\-])\s?(-?\d+)").unwrap();
  let re_prod = Regex::new(r"(-?\d+)\s?([\*|/])\s?(-?\d+)").unwrap();

  // Tomar datos de usuario
  println!("Ingresar expresión: ");
  let mut expression = String::new();
  std::io::stdin().read_line(&mut expression).unwrap();

  // Interpretar expresión y operar
  expression = read(expression, re_prod);
  expression = read(expression, re_add);

  // Mostrar resultados
  println!("Resultado: {}", expression)
}

fn read(mut exp: String, re: Regex) -> String {
  loop {
    let caps = re.captures(exp.as_str());

    if caps.is_none() {
      break;
    }

    let caps = caps.unwrap();

    let caps_exp = caps.get(0).unwrap().as_str();
    let left_value: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
    let operator: &str = caps.get(2).unwrap().as_str();
    let right_value: i32 = caps.get(3).unwrap().as_str().parse().unwrap();

    let res: i32 = operate(left_value, right_value, operator);
    exp = exp.replace(caps_exp, &res.to_string());
  }

  return exp;
}

fn operate(a: i32, b: i32, op: &str) -> i32 {
  match op {
    "+" => a + b,
    "-" => a - b,
    "*" => a * b,
    "/" => a / b,
    _ => 0,
  }
}

Esta es mi solución al proyecto. Utilizo una sola regex para todas las operaciones matemáticas introduciendo un nuevo wildcard. Además agrego la opción de entender comandos, “quit” para salir, “help” para obtener ayuda. Apreciaría sus comentarios.

use regex::Regex;
use std::io::{stdin};

fn main() {
    println!("Bienvenido o bienvenida.");
    println!("Introduzca una expresion matematica o pida ayuda con 'help'.");
    loop {
        // Regex definition
        let re_math = Regex::new(r"(\d+)\s?(\+|-|\*|/)\s?(\d+)").unwrap();
        let re_commands = Regex::new(r"(quit|help)").unwrap();

        // Operation Capture
        let mut exp = String::new();
        stdin().read_line(&mut exp).unwrap();
        let exp = exp.trim();

        // Regex Commands
        let commands_caps = re_commands.captures(exp);
        if commands_caps.is_some() {
            let command = commands_caps.unwrap().get(1).unwrap().as_str();
            match command {
                "quit" => break,
                "help" => println!("Introduzca una expresión matemática"),
                _ => println!("Comando no reconocido"),
            }
            continue
        }

        // Regex Match
        let operation_caps = re_math.captures(exp);
        if operation_caps.is_some() {
            let caps = operation_caps.unwrap();
            let elem1: i32 = caps.get(1).unwrap().as_str().parse().unwrap();
            let elem2: i32 = caps.get(3).unwrap().as_str().parse().unwrap();
            let op: &str = caps.get(2).unwrap().as_str();

            match op {
                "+" => println!("=> {}", elem1 + elem2),
                "-" => println!("=> {}", elem1 - elem2),
                "*" => println!("=> {}", elem1 * elem2),
                "/" => println!("=> {}", elem1 / elem2),
                _ => println!("Operación no reconocida"),
            }

            continue
        }

        println!("Expresión no reconocida. Escriba 'help' para obtener ayuda.");
    }
}

Better TOML, plugin VScode.

Aquí dejo mi ejercicio donde también añado soporte para el uso de paréntesis, números negativos y con una implementación que trata de asegurar el orden correcto de las operaciones. Lo que hice primeramente fue crear una función auxiliar para generalizar la aplicación de las operaciones básicas siguiendo el orden correcto: ```js fn get_operation(input: &str) -> (&str, Regex) { let re_add = Regex::new(r"(-?\d+)\s*\+\s*(-?\d+)").unwrap(); // Regex to find addition let re_subs = Regex::new(r"(-?\d+)\s*\-\s*(-?\d+)").unwrap(); // Regex to find substraction let re_mult = Regex::new(r"(-?\d+)\s*\*\s*(-?\d+)").unwrap(); // Regex to find multiplication let re_div = Regex::new(r"(-?\d+)\s*\/\s*(-?\d+)").unwrap(); // Regex to find division let re_void = Regex::new(r"(\d+)").unwrap(); // Arbitrary regex to return if no operation is found // Check which are the first operations we should solve (following the order of operations) if re_mult.is_match(&input) { return ("mult", re_mult); } else if re_div.is_match(&input) { return ("div", re_div); } // For addition and substraction, we need to check which one is closer to the beginning else if re_add.is_match(&input) || re_subs.is_match(&input) { // Check which operation is closer to the beginning let add = re_add.find(&input); let subs = re_subs.find(&input); if add.is_some() && subs.is_some() { let add = add.unwrap(); let subs = subs.unwrap(); if add.start() < subs.start() { return ("add", re_add); } else { return ("subs", re_subs); } } else if add.is_some() { return ("add", re_add); } else { return ("subs", re_subs); } } // If no operation is found, return a value of "none" and a regex that will return the number else { return ("none", re_void); } } ```Esta función recibe un string y devuelve una tupla con un otro string identificando cuál sería la operación a aplicar primero y la expresión regular para poder detectarla y aplicarla. Adicionalmente, cree otra función que recibe como parámetros el string a aplicar la operación, el tipo de operación a aplicar y la expresión regular para aplicarla. Y con base en ello nos devuelve el string original, pero con la operación aplicada, es decir, si por ejemplo tenemos un string de la forma "4\\\*5-2", al pasar por la primera función nos dirá que la operación a aplicar primero es la multiplicación, y al pasar por la segunda nos devolverá un string de la forma "10-2": ```js fn apply_operation(input: &str, operation: &str, re: Regex) -> String { // Replace the operation with the result of the operation let result = re.replace_all(input, |caps: &regex::Captures| { let a: i32 = caps[1].parse().unwrap(); let b: i32 = caps[2].parse().unwrap(); match operation { "add" => (a + b).to_string(), "subs" => (a - b).to_string(), "mult" => (a * b).to_string(), "div" => (a / b).to_string(), _ => panic!("Invalid operation"), } }); return result.to_string(); } ```El código aplica algunas cosas que aún no se han visto en el curso y que investigue por mi cuenta, pero realmente hace algo muy parecido a lo que se hizo en clase solo que use lo que se conoce como "Closures" (`|caps: \&regex::Captures|` ) los cuales, para los que tengan experiencia en Python, son bastante similares a las funciones lambda; y para los que no, son básicamente funciones que se crean dentro de otra función. En este caso, la función toma los caps del input con base en la expresión regular que pasamos como parámetro, saca de ahí los 2 valores a operar (a, b) y realiza la operación necesaria que también pasamos por parámetro. La tercera y última función auxiliar que cree fue la de resolver paréntesis, que básicamente toma cada uno de los paréntesis, los resuelve usando las 2 funciones anteriores, y remplaza los paréntesis por un único valor resultado de las operaciones interiores: ```js fn solve_parentheses(input: String) -> String { // Regex to find expressions inside parentheses let re_parentheses = Regex::new(r"\(([^()]*)\)").unwrap(); // New variable to store the modified input let mut new_input: String = input.clone(); // Loop until there are no more parentheses if re_parentheses.is_match(&input) { // Loop through each parentheses and solve it for caps in re_parentheses.captures_iter(&input) { // Save the full expression inside the parentheses let mut operation = caps[1].to_string(); // Loop until there are no more operations inside the parentheses loop { // Get the operation and the regex to solve it let (operation_name, re) = get_operation(&operation); // If there are no more operations, replace the parentheses with the result if operation_name == "none" { // Replace the parentheses with the result new_input = re_parentheses.replace(&new_input, &operation).to_string(); // Exit the loop break; } else { // If there are more operations, solve them let result = apply_operation(&operation, operation_name, re); operation = result.to_string(); } } } } // Return the modified input without parentheses return new_input; } ```Finalmente, la función main se encarga de recoger el input del usuario, llamar a las funciones necesarias, y finalmente tratar de convertir el valor a un entero, y en caso de que no sea posible, devolver un error: ```js fn main() { // Get user data println!("Enter an operation:"); let mut input = String::new(); // Read user input io::stdin() .read_line(&mut input) .expect("Failed to read line"); // Do parentheses operations first let mut new_input = solve_parentheses(input.clone()); // Loop until there are no more operations loop { let (operation_name, re) = get_operation(&new_input); if operation_name == "none" { break; } let result = apply_operation(&new_input, operation_name, re); new_input = result.to_string(); } // Cast to i32 let new_input: i32 = new_input .trim() .parse() .expect("Invalid operation, please try again"); // Print the result println!("\nResult: {}", new_input); } ```Hay bastantes mejoras que se le pueden hacer al código, se podría agregar una función auxiliar extra que realice el loop de todas las operaciones y así se podría usar tanto dentro de la función para resolver los paréntesis, como en el main para evitar escribir el mismo loop en ambos lados. Además de la expresión regular para detectar los paréntesis, modifique las expresiones regulares del resto de operaciones, por una parte, cambiando el `\s? `por `\s` debido a que este permite validar expresiones que tengan más de un espacio entre medias (por ejemplo "1 +5\\\*2"); Y la otra parte de las expresiones que modifique fue cambiar los `-?\d+` por `-?\d+` . Esto ya que a veces, cuando obtenía números negativos en las operaciones, no los tenía en cuenta, por ejemplo si se le pasaba un string del tipo "-2+5" daría como resultado -7 porque tomaría la operación como "2+5" y luego le aplicaba el negativo. El código completo lo pueden encontrar aquí: <https://github.com/JsNcAr/Platzi/blob/main/Rust/calculadora/src/main.rs> Para los que no tengan mucha experiencia con la programación, no se asusten si sienten este código demasiado complicado, en mi caso lo pude hacer debido a que tengo bastante experiencia con otros lenguajes y quería intentar algo similar a lo que haría en los lenguajes que domino más, pero si no pudieron lograr el reto o tienen el código lleno de errores, es normal y no tiene nada malo, todos pasamos por esa época y el poder hacer códigos más sofisticados está limitado únicamente por el tiempo que le dediques a programar.