重构解析器时,由于变量已被借用为不可变的,因此无法将其作为可变的变量借出。

4
为了学习Rust,我开始着手处理一个简单的解析器。我确实使它正常运行了,但我使用了许多不好的实践方法,比如在我的结构体中使用.clone()来保存数据。
今天,我开始重构代码,使用对象的引用而不是克隆它们。在遇到这两个错误之前,我已经做了很长一段路程:
error: cannot borrow 'm_list' as mutable because it is also borrowed as immutable [E0502]

并且

error: cannot borrow 'h_list' as mutable because it is also borrowed as immutable [E0502]

在阅读了关于这个错误的信息后,我感到非常困惑。有些人说这是Rust借用检查器的bug,但我99%确定这是我的代码中存在的bug。

出现错误的代码:

mod utypes;

use std::env;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::error::Error;

use utypes::*;

fn main() {    

    let args: Vec<_> = env::args().collect();

    let file = match File::open(&args[1]) {

        Ok(file) => file,
        Err(why) => panic!("IO Error on line: {}, cause: {}", line!(), Error::description(&why)),

    };
    let buffer = BufReader::new(file);

    let mut m_list: Vec<Materia> = Vec::new(); 
    let mut h_list: Vec<Hora> = Vec::new();
    let mut t_list: Vec<Turno> = Vec::new();

    for line in buffer.lines() {

        let l = line.unwrap();
        let spl_line: Vec<&str> = l.split(':').collect();

        if spl_line[0].starts_with('#') { continue; }

        match spl_line[0] {

            "mat" => { parse_mat(&mut m_list,spl_line) },
            "hra" => { parse_hra(&m_list,&mut h_list,spl_line) },
            "trn" => { parse_trn(&mut t_list,spl_line) },
            "tad" => { exec_tad(&h_list,&mut t_list,spl_line) },
            ""    => continue,
            _     => panic!("Unknown type identifier: {}.", spl_line[0]),

        }

    }

    for turno in t_list.iter() {

        println!("\nTurno: {}:\n", turno.nombre);
        for dia in turno.dias.iter() {

            print!("{:?}: ", dia.enum_id);
            for hora in dia.horas.iter() {

                print!("\n\t{} ", hora);

            }
            println!("");

        }

    }

    /*
    println!("\nDEBUG INFO:\n");
    println!("{:?}", m_list);
    println!("{:?}", h_list);
    println!("{:?}", t_list);
    */

}

fn get_trn( t_list: &Vec<Turno>, gid: u32 ) -> Option<usize> {

    for (i,trn) in t_list.iter().enumerate() {

        if trn.id == gid {

            return Some(i);

        }

    }

    None

}

fn get_hra ( h_list: &Vec<Hora>, gid: u32 ) -> Option<usize> {

    for (i,hra) in h_list.iter().enumerate() {

        if hra.id == gid {

            return Some(i);

        }

    }

    None

}

fn get_mat ( m_list: &Vec<Materia>, gid: u32 ) -> Option<usize> {

    for (i,mat) in m_list.iter().enumerate() {

        if mat.id == gid {

            return Some(i);

        }

    }

    None

}

fn parse_mat<'a> ( m_list: &'a mut Vec<Materia>, line: Vec<&str> ) {

    assert_eq!(4,line.len());

    let id: u32 = match line[1].parse::<u32>() {

        Ok(id)    => id,
        Err(_)    => panic!("Error parsing u32 at {}.", line!()),

    };   

    m_list.push(Materia::new(id,line[2].to_string(),line[3].to_string()));

    }

fn parse_hra<'a> ( m_list: &'a Vec<Materia>, h_list: &mut Vec<Hora<'a>>, line: Vec<&str> ) {

    assert_eq!(5,line.len());

    let id: u32 = match line[1].parse::<u32>() {

        Ok(id)    => id,
        Err(_)    => panic!("Error parsing u32 at {}.", line!()),

    };
    let start: u32 = match line[2].parse::<u32>() {

        Ok(start) => start,
        Err(_)    => panic!("Error parsing u32 at {}.", line!()),

    };
    let durat: u32 = match line[3].parse::<u32>() {

        Ok(durat) => durat,
        Err(_)    => panic!("Error parsing u32 at {}.", line!()),

    };
    let matid: u32 = match line[4].parse::<u32>() {

        Ok(matid) => matid,
        Err(_)    => panic!("Error parsing u32 at {}.", line!()),

    };
    let mat_i: usize = match get_mat(m_list,matid) {

        Some(x) => x,
        None      => panic!("Error matid not found in m_list!")

    };

    h_list.push(Hora::new(id,start,durat,&m_list[mat_i]));

}

fn parse_trn<'a> ( t_list: &mut Vec<Turno<'a>>, line: Vec<&str> ) {

    assert_eq!(3,line.len());

    let id: u32 = match line[1].parse::<u32>() {

        Ok(id) => id,
        Err(_) => panic!("Error parsing u32 at {}.", line!()),

    };

    t_list.push(Turno::new(id,line[2].to_string()));

}

fn exec_tad<'a> ( h_list: &'a Vec<Hora<'a>>, t_list: &mut Vec<Turno<'a>>, line: Vec<&str> ) {

    assert_eq!(4,line.len());

    let hid: u32 = match line[2].parse::<u32>(){

        Ok(hid) => hid,
        Err(_)  => panic!("Error parsing u32 at {}.", line!()),

    };
    let tid: u32 = match line[3].parse::<u32>(){

        Ok(tid) => tid,
        Err(_)  => panic!("Error parsing u32 at {}.", line!()),

    };

    let hra_i: usize = match get_hra(h_list,hid) {

        Some(x) => x,
        None    => panic!("Error matid not found in m_list!")

    };
    let trn_i: usize = match get_trn(t_list,tid) {

        Some(x) => x,
        None    => panic!("Error matid not found in m_list!")

    };

    match line[1] {

        "Dom" => t_list[trn_i].addhra(Dias::Dom,&h_list[hra_i]),
        "Lun" => t_list[trn_i].addhra(Dias::Lun,&h_list[hra_i]),
        "Mar" => t_list[trn_i].addhra(Dias::Mar,&h_list[hra_i]),
        "Mie" => t_list[trn_i].addhra(Dias::Mie,&h_list[hra_i]),
        "Jue" => t_list[trn_i].addhra(Dias::Jue,&h_list[hra_i]),
        "Vie" => t_list[trn_i].addhra(Dias::Vie,&h_list[hra_i]),
        "Sab" => t_list[trn_i].addhra(Dias::Sab,&h_list[hra_i]),
        _     => panic!("Unknown day error!") 

    }

}

还有utypes.rs文件:

use std::fmt;

//Dias
#[derive(Debug)]
pub enum Dias {

    Dom,
    Lun,
    Mar,
    Mie,
    Jue,
    Vie,
    Sab,

}

//Materia

#[derive(Debug)]
pub struct Materia {

    pub id: u32,
    pub nombre: String,
    pub profesor: String,

}

impl Materia {

    pub fn new( i: u32, nom: String, prof: String ) -> Materia {

        Materia {

            id: i,
            nombre: nom,
            profesor: prof,


        }

    }
}

//Hora

#[derive(Debug,Clone)]
pub struct Hora<'a> {

    pub id: u32,
    pub comienzo: u32,
    pub duracion: u32,
    pub materia: &'a Materia,

}

impl<'a> Hora<'a> {

    pub fn new ( id: u32, com: u32, dur: u32, mat: &'a Materia ) -> Hora<'a> {

        Hora {

           id: id,
            comienzo: com,
            duracion: dur,
            materia: mat,

        }

    }

    pub fn fin ( &self ) -> u32 {

        self.comienzo + self.duracion

    }

    pub fn fmt_time ( tot: u32 ) -> String {

        let min = ( tot / 60 ) % 60;
        let hra = tot / 3600;

        format!("{:02}:{:02}", hra, min)

    }

}

impl<'a> fmt::Display for Hora<'a> {

    fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result {

        write!(f, "[{}-{}, {}]", Hora::fmt_time(self.comienzo), Hora::fmt_time(self.fin()), self.materia.nombre)

    }

}

//Dia

#[derive(Debug)]
pub struct Dia<'a> {

    pub enum_id: Dias,
    pub nombre: String,
    pub horas: Vec<&'a Hora<'a>>,

}

impl<'a> Dia<'a> {

    pub fn new( ei: Dias, nom: String ) -> Dia<'a> {

        Dia {

            enum_id: ei,
            nombre: nom,
            horas: Vec::new(),

        }

    }

    pub fn addhra( &mut self, hra: &'a Hora<'a> ){

        self.horas.push(hra);

    }   

}

//Turno

#[derive(Debug)]
pub struct Turno<'a> {

    pub id: u32,
    pub nombre: String,
    pub dias: [Dia<'a>; 7],

}

impl<'a> Turno<'a> {

    pub fn new( i: u32, nom: String ) -> Turno<'a> {

        Turno {

            id: i,
            nombre: nom,
            dias: [

                    Dia::new(Dias::Dom,"Domingo"  .to_string()),
                    Dia::new(Dias::Lun,"Lunes"    .to_string()),
                    Dia::new(Dias::Mar,"Martes"   .to_string()),
                    Dia::new(Dias::Mie,"Miercoles".to_string()),
                    Dia::new(Dias::Jue,"Jueves"   .to_string()),
                    Dia::new(Dias::Vie,"Viernes"  .to_string()),
                    Dia::new(Dias::Sab,"Sabado"   .to_string())

                   ],

        }

    }

    pub fn addhra( &mut self, dia: Dias, hra: &'a Hora<'a> ) {

        match dia {

            Dias::Dom => self.dias[0].addhra(hra),
            Dias::Lun => self.dias[1].addhra(hra),
            Dias::Mar => self.dias[2].addhra(hra),
            Dias::Mie => self.dias[3].addhra(hra),
            Dias::Jue => self.dias[4].addhra(hra),
            Dias::Vie => self.dias[5].addhra(hra),
            Dias::Sab => self.dias[6].addhra(hra),

        }

    }

}

我认为可变引用 &mut m_list 在函数 parse_mat() 返回之后就会结束,所以在 for 循环的另一次迭代中,我应该能够调用 parse_hra() 并传递 &m_list 而不会出现问题,对吗?
完整错误信息:
src/main.rs:36:39: 36:45 error: cannot borrow `m_list` as mutable because it is also borrowed as immutable [E0502]
src/main.rs:36             "mat" => { parse_mat(&mut m_list,spl_line) },
                                                 ^~~~~~
src/main.rs:37:35: 37:41 note: previous borrow of `m_list` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `m_list` until the borrow ends
src/main.rs:37             "hra" => { parse_hra(&m_list,&mut h_list,spl_line) },
                                             ^~~~~~
src/main.rs:71:2: 71:2 note: previous borrow ends here
src/main.rs:11 fn main() {    
           ...
src/main.rs:71 }
           ^
src/main.rs:37:47: 37:53 error: cannot borrow `h_list` as mutable because it is also borrowed as immutable [E0502]
src/main.rs:37             "hra" => { parse_hra(&m_list,&mut h_list,spl_line) },
                                                         ^~~~~~
src/main.rs:39:34: 39:40 note: previous borrow of `h_list` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `h_list` until the borrow ends
src/main.rs:39             "tad" => { exec_tad(&h_list,&mut t_list,spl_line) },
                                            ^~~~~~
src/main.rs:71:2: 71:2 note: previous borrow ends here
src/main.rs:11 fn main() {    
           ...
src/main.rs:71 }
           ^

我不是母语使用者,如有任何错误请见谅。

play-rust


请提供完整的错误代码示例,包括parse_hra/parse_mat/parse_trn/exec_tad的存根方法,不要删除main/uses。 - Demur Rumed
1
你可以使用函数parse_hram_list的元素引用存储在h_list中。因此,当h_list超出作用域时,m_list的借用也会结束。建议使用Rc代替引用。 - aSpex
1个回答

1

@aSpex的评论是正确的。

要解决这个问题,您可以使用相应的索引替换HoraDia内部的引用。也就是说:

struct Hora {
    materia: usize; //index in m_list 
}

struct Dia {
    horas: Vec<usize>; //indices in h_list
}

你也可以创建一个带有 m_listh_listt_list 字段的结构体,这样它们就可以一起保存。

请参见 this,了解使用 RcRefCell 方法的比较。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接