es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

El comprobador de préstamos evita la mutación y el rastreo de valores dentro de un bucle anidado.

Estoy tratando de hacer un bucle anidado que mute objetos, salga del bucle en algún punto y lleve un registro de los últimos valores encontrados. El siguiente ejemplo inventado da una idea.

El problema que tengo es que el chequeador de préstamos no me permite recorrer la colección y también devolver un valor fuera de ella. Logré resolverlo haciendo un seguimiento de los índices de los últimos valores, pero eso parece una solución chapucera y no funcionaría si solo tuviera iteradores en lugar de Vector. También intenté usar bucles anidados de try_fold(), que se encontraron con el mismo problema.

Pregunta principal: ¿Cómo podemos hacer que esto funcione sin recurrir a los índices?

Pregunta adicional: ¿Es el código tal como está realmente seguro? Es decir, ¿el código está roto o está bien y el chequeador de préstamos simplemente no es lo suficientemente inteligente como para darse cuenta de eso?

use rand::Rng;

fn main() {
    let randos = std::iter::from_fn(|| Some(rand::thread_rng().gen_range(0..10)));
    let mut accumulators = vec![Accumulator { count: 0 }, Accumulator { count: 0 }];
    let mut winning_accumulator = None;
    let mut last_num = None;

    for num in randos {
        // La siguiente línea compila con error
        // error[E0499]: no se puede mutar `accumulators` más de una vez a la vez
        // `accumulators` fue prestado de forma mutable aquí en la iteración anterior del bucle
        for acc in &mut accumulators {
            if acc.add(num) > 100 {
                winning_accumulator = Some(acc);
                last_num = Some(num);
                break;
            }
        }
    }

    println!(
        "Último número: {}, total: {}",
        last_num.unwrap(),
        winning_accumulator.unwrap().count
    );
}

struct Accumulator {
    count: u64,
}

impl Accumulator {
    fn add(&mut self, val: u64) -> u64 {
        // finge que estamos haciendo algo complicado aquí
        self.count += val;
        self.count
    }
}
Tags:  ,

Answer

  1. Avatar for davy.ai

    Para que esto funcione sin recurrir a índices, podemos usar la función enumerate() proporcionada por los iteradores. Esta función toma un iterador como entrada y devuelve un nuevo iterador que devuelve cada elemento del iterador original junto con un índice que representa su posición en el iterador original. Al usar esta función, podemos recorrer la colección y también devolver un valor sin encontrarnos con el error del comprobador de préstamos.

    Aquí hay una versión actualizada del código que utiliza enumerate():

    use rand::Rng;
    
    fn main() {
        let randos = std::iter::from_fn(|| Some(rand::thread_rng().gen_range(0..10)));
        let mut accumulators = vec![Accumulator { count: 0 }, Accumulator { count: 0 }];
        let mut winning_accumulator = None;
        let mut last_num = None;
    
        for (i, num) in randos.enumerate() {
            for acc in &mut accumulators {
                if acc.add(num) > 100 {
                    winning_accumulator = Some(acc);
                    last_num = Some(num);
                    break;
                }
            }
            if winning_accumulator.is_some() {
                break;
            }
        }
    
        println!(
            "Último número: {}, total: {}",
            last_num.unwrap(),
            winning_accumulator.unwrap().count
        );
    }
    
    struct Accumulator {
        count: u64,
    }
    
    impl Accumulator {
        fn add(&mut self, val: u64) -> u64 {
            //fingamos que estamos haciendo algo complicado aquí
            self.count += val;
            self.count
        }
    }
    

    Con respecto a la pregunta extra, el código tal como está escrito es seguro. El comprobador de préstamos impide correctamente préstamos mutables de accumulators que se superpongan entre iteraciones del bucle for, lo cual podría conducir a un comportamiento indefinido. La solución alternativa utilizando índices también es segura, pero es menos idiomática y puede propiciar errores si se modifica el código en el futuro.

Comments are closed.