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]
, потому что он разбивает фрагмент и дает оставшуюся часть для дополнительных записей. детская площадка
Какой был бы наиболее элегантный способ получить фактический результат после многих операций записи, а не оставшийся фрагмент? Я не хочу отслеживать, сколько байт было записано, просто чтобы можно было разрезать вывод буфера.
Контекст: микроконтроллеры не позволяют выделять память, поэтому я передаю для записи срез буфера в худшем случае, и он почти никогда не заполняется до конца.
Измените перезаимствование, чтобы 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]
Вероятно, было бы более расширяемым и эргономичным, если бы вы просто возвращали записанные байты и нарезали себя, но каждому свое.
Вариант 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ˋ, чтобы снизить вероятность ошибок? Если делать много раз для каждого звонка, что-то может быть сделано не так
@GuerlandoOCs Я отредактировал свой ответ, добавив еще один пример.
да, но проблема в том, что он печатает все, а я хочу напечатать только то, сколько было написано. Я мог бы вернуть длину to_bytes и разделить выходные данные, но есть ли автоматический способ, чтобы выходные данные уже были разделены на правильную длину?