Абстрагирование изменяемых/неизменяемых ссылок в Rust

Мне нужно избавиться от дублирования в этом коде:

pub struct Memory {
    layout: MemoryLayout,
    rom: Vec<u8>,
    ram: Vec<u8>,
}

impl Memory {
    pub fn get_mem_vec_ref(&self, address: u32) -> Result<&Vec<u8>, RiscvError> {
        // ...

        let mem_vec_ref = match address {
            addr if (rom_start..rom_end).contains(&addr) => Ok(&self.rom),
            addr if (ram_start..ram_end).contains(&addr) => Ok(&self.ram),
            addr => Err(RiscvError::MemoryAlignmentError(addr)),
        }?;

        return Ok(mem_vec_ref);
    }

    pub fn get_mem_vec_mut_ref(&mut self, address: u32) -> Result<&mut Vec<u8>, RiscvError> {
        // ...

        let mem_vec_ref = match address {
            addr if (rom_start..rom_end).contains(&addr) => Ok(&mut self.rom),
            addr if (ram_start..ram_end).contains(&addr) => Ok(&mut self.ram),
            addr => Err(RiscvError::MemoryAlignmentError(addr)),
        }?;

        return Ok(mem_vec_ref);
    }
}

Как я могу абстрагироваться, используя изменяемую или неизменяемую ссылку на себя? Могут ли Box или RefCell помочь в этом случае?

Изменяет ли get_mem_vec_mut_refself?

Schwern 01.03.2024 04:00

Нет, он возвращает только изменяемую ссылку на внутренний вектор структуры. &self не будет работать внутри get_mem_vec_mut_ref, потому что тогда я не смогу заимствовать &mut self.rom изменчиво.

Tony I. 01.03.2024 11:41
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
2
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку в обоих случаях вы имеете дело со ссылками, вы можете определить общую функцию, где T будет либо &Vec<u8>, либо &mut Vec<u8>. Итак, вы можете сделать что-то вроде этого:

fn get_mem<T>(address: u32, rom: T, ram: T) -> Result<T, RiscvError> {
    // ...

    match address {
        addr if (rom_start..rom_end).contains(&addr) => Ok(rom),
        addr if (ram_start..ram_end).contains(&addr) => Ok(ram),
        addr => Err(RiscvError::MemoryAlignmentError(addr)),
    }
}

impl Memory {
    pub fn get_mem_vec_ref(&self, address: u32) -> Result<&Vec<u8>, RiscvError> {
        // ...

        let mem_vec_ref = get_mem(address, &self.rom, &self.ram)?;

        return Ok(mem_vec_ref);
    }

    pub fn get_mem_vec_mut_ref(&mut self, address: u32) -> Result<&mut Vec<u8>, RiscvError> {
        // ...

        let mem_vec_ref = get_mem(address, &mut self.rom, &mut self.ram)?;

        return Ok(mem_vec_ref);
    }
}

Теперь, очевидно, вам нужно изменить get_mem(), чтобы учесть rom_start, rom_end, ram_start, ram_end. Если вы хотите избежать необходимости передавать 100 полей в get_mem(), возможно, вместо этого стоит ввести новый тип для работы с адресами, например. что-то вроде:

struct Addr {
    // ...
}

impl Addr {
    fn get_mem<T>(&self, rom: T, ram: T) -> Result<T, RiscvError> {
        // ...
    }
}

Таким образом, когда я добавляю другой тип памяти, мне нужно будет добавить аргументы в функцию get_mem. Я хотел бы избежать этого, если это возможно.

Tony I. 01.03.2024 11:48

@TonyI. Мне нужно больше контекста, чтобы дать лучшее предложение. Когда вы говорите о другом типе памяти, вы имеете в виду другой Vec<u8> в Memory или совершенно отдельный тип? Кроме того, я предполагаю, что *_start и _end связаны с Vec<u8> того же имени. Так что возможно им стоит быть вместе в 1 типе. Опять же, я могу размышлять о многом, но мне понадобится больше контекста, чтобы дать лучшие идеи. Итак, на данный момент, если вы просто хотите удалить предоставленный вами дублирующийся код, вы можете сделать это следующим образом.

vallentin 01.03.2024 15:39

Под дополнительными типами памяти я подразумеваю что-то вроде отображаемого в память адресного пространства ввода-вывода, добавленного в Memory struct. Ваше решение действительно работает хорошо, теперь, когда я думаю о добавлении 1-2 аргументов, в дальнейшем не будет больших накладных расходов. Я буду использовать его, чтобы получить +1 и принять от меня.

Tony I. 01.03.2024 16:15

Другие вопросы по теме