Как много раз записать_all в срез в Rust и получить результат

use std::io::Write;

pub struct A{
    pub a: u8,
    pub b: u16
}

impl A {
    pub fn to_bytes(&self, output: &mut &mut[u8]) -> Result<(), std::io::Error> {
        output.write_all(&[self.a])?;
        output.write_all(&self.b.to_be_bytes()[..])?;
        Ok(())
    }
}

fn main() {
    let mut buffer: &mut [u8] = &mut [0u8; 10];
    let a = A{
        a: 7,
        b: 100
    };
    a.to_bytes(&mut buffer);
    println!("{:?}", buffer);
}

это печатает [0, 0, 0, 0, 0], потому что он разбивает фрагмент и дает оставшуюся часть для дополнительных записей. детская площадка

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

Контекст: микроконтроллеры не позволяют выделять память, поэтому я передаю для записи срез буфера в худшем случае, и он почти никогда не заполняется до конца.

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
77
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Измените перезаимствование, чтобы buffer сохранил исходный диапазон:

impl A {
    pub fn to_bytes(&self, mut output: &mut[u8]) -> Result<(), std::io::Error> {
        ...             // ^^^
    }
}

fn main() {
    ...
    a.to_bytes(buffer);
    ...
}

Он напечатает:

[7, 0, 100, 0, 0, 0, 0, 0, 0, 0]

ссылка на игровую площадку


Если вы хотите записать данные, отслеживайте, сколько байтов было записано, и используйте это для воздействия на buffer на месте; вы можете сделать это так:

impl A {
    pub fn to_bytes(&self, buffer: &mut &mut[u8]) -> Result<(), std::io::Error> {
        let mut output = &mut **buffer;
        
        let space = output.len();
        
        output.write_all(&[self.a])?;
        output.write_all(&self.b.to_be_bytes()[..])?;
        
        let written = space - output.len();
        
        // see https://stackoverflow.com/questions/61223234/can-i-reassign-a-mutable-slice-reference-to-a-sub-slice-of-itself
        // for in-place mutable sub-slicing
        let orig = std::mem::replace(buffer, &mut []);
        *buffer = &mut orig[..written];
        
        Ok(())
    }
}

с вашим кодом напечатает:

[7, 0, 100]

ссылка на игровую площадку

Вероятно, было бы более расширяемым и эргономичным, если бы вы просто возвращали записанные байты и нарезали себя, но каждому свое.

да, но проблема в том, что он печатает все, а я хочу напечатать только то, сколько было написано. Я мог бы вернуть длину to_bytes и разделить выходные данные, но есть ли автоматический способ, чтобы выходные данные уже были разделены на правильную длину?

Guerlando OCs 30.08.2024 03:42

Вариант 1: Используйте std::io::Cursor. Курсор имеет «текущую позицию» для чтения/записи. Позиция после записи сообщает нам, сколько байт было записано.

use std::io::{self, Cursor, Write};

pub struct A {
    pub a: u8,
    pub b: u16,
}

impl A {
    pub fn to_bytes(&self, output: &mut Cursor<&mut [u8]>) -> io::Result<()> {
        output.write_all(&[self.a])?;
        output.write_all(&self.b.to_be_bytes()[..])?;
        Ok(())
    }
}

fn main() {
    let mut buffer: &mut [u8] = &mut [0u8; 10];
    let mut cursor = Cursor::new(buffer);
    let a = A { a: 7, b: 100 };
    a.to_bytes(&mut cursor);
    println!("{:?}", &cursor.get_ref()[..cursor.position() as usize]);
}

Вариант 2. Сделайте копию (или, точнее, перезаимствуйте) весь фрагмент в другой переменной и передайте его в to_bytes, затем вычислите разницу в длине между двумя фрагментами, чтобы узнать, сколько байтов было записано.

use std::io::{self, Write};

pub struct A {
    pub a: u8,
    pub b: u16,
}

impl A {
    pub fn to_bytes(&self, output: &mut &mut [u8]) -> io::Result<()> {
        output.write_all(&[self.a])?;
        output.write_all(&self.b.to_be_bytes()[..])?;
        Ok(())
    }
}

fn main() {
    let mut buffer: &mut [u8] = &mut [0u8; 10];
    let mut buffer2: &mut [u8] = &mut buffer;
    let a = A { a: 7, b: 100 };
    a.to_bytes(&mut buffer2);
    // `buffer2.len()` needs to be evaluated in a separate statement...
    let remaining_len = buffer2.len();
    // ...so that `buffer2`'s lease can end,
    // which lets us use `buffer` again.
    println!("{:?}", &buffer[..(buffer.len() - remaining_len)]);
}

В обоих случаях результат равен [7, 0, 100].


Есть ли способ выполнить перезаимствование внутри реализации ˋto_bytesˋ, чтобы снизить вероятность ошибок?

Да, мы можем переместить эту логику внутрь to_bytes и to_bytes вернуть срез, содержащий записанные данные.

use std::io::{self, Write};

pub struct A {
    pub a: u8,
    pub b: u16,
}

impl A {
    pub fn to_bytes<'a>(&self, output: &'a mut [u8]) -> io::Result<&'a mut [u8]> {
        let mut output_tail: &mut [u8] = &mut *output;
        output_tail.write_all(&[self.a])?;
        output_tail.write_all(&self.b.to_be_bytes()[..])?;
        let remaining_len = output_tail.len();
        let total_len = output.len();
        Ok(&mut output[..(total_len - remaining_len)])
    }
}

fn main() {
    let mut buffer: &mut [u8] = &mut [0u8; 10];
    let a = A { a: 7, b: 100 };
    println!("{:?}", a.to_bytes(&mut buffer));
}

Есть ли способ выполнить перезаимствование внутри реализации ˋto_bytesˋ, чтобы снизить вероятность ошибок? Если делать много раз для каждого звонка, что-то может быть сделано не так

Guerlando OCs 30.08.2024 06:08

@GuerlandoOCs Я отредактировал свой ответ, добавив еще один пример.

Francis Gagné 31.08.2024 05:12

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