Я реализую интерфейс сжатия данных:
pub trait NumericEncoder<V> {
fn encode(&mut self, value: V) -> io::Result<()>;
}
Кодер может закодировать какое-то число в каком-то виде вывода, где вывод может быть потоком (файлом), байтовым буфером или даже другим кодировщиком. Можно было бы вызвать такую реализацию:
let f = File::create("out").unwrap();
// Delta encoder whose data is run-length-compressed
let mut enc = DeltaEncoder::new(RunLengthEncoder::new(f));
enc.encode(123).unwrap();
Это все хорошо, но в некоторых случаях мне нужно несколько кодировщиков для одного и того же выходного потока. Что-то вроде (упрощенно):
let f = File::create("out")?;
let mut idEnc = RunLengthEncoder::new(DeltaEncoder::new(f));
let mut dataEnc = LZEncoder::new(f);
for (id, data) in input.iter() {
idEnc.encode(id);
dataEnc.encode(data);
}
Здесь два кодировщика будут чередовать свои данные по мере их записи.
Для этого требуется изменяемый доступ к одному и тому же файлу, что невозможно с прямыми ссылками &mut
. Насколько я могу судить, это можно сделать только с помощью RefCell
; Есть ли способ лучше?
Насколько я могу судить, это сделало бы все реализации кодировщика менее чистыми. Прямо сейчас кодировщик может быть объявлен следующим образом:
pub struct MySpecialEncoder<'a, V, W>
where
W: io::Write,
{
w: &'a mut W,
phantom: std::marker::PhantomData<V>,
}
С RefCell
каждая структура кодировщика и конструктор должны иметь дело с Rc<RefCell<W>>
, что не так хорошо и пропускает совместное использование модуля записи в кодировщик, которому не нужно знать, что модуль записи является общим.
(Я подумал, могу ли я изменить трейт NumericEncoder
, чтобы он принимал аргумент записи, который должен быть std::io::Write
. Это не сработает, потому что некоторые кодировщики записывают не в std::io::Write
, а в другой NumericEncoder
.)
idEnc.encode(f, id);
dataEnc.encode(f, data);
это обеспечивает большую гибкость. - person Stargateur   schedule 14.04.2019NumericEncoder
дляT: io::Write
? Затем измените его подпись, чтобы принять другойNumericEncoder
- person Laney   schedule 14.04.2019snake_case
для переменных, методов, макросов, полей и модулей;UpperCamelCase
для типов и вариантов перечисления; иSCREAMING_SNAKE_CASE
для статики и констант. Используйте вместо этогоid_enc
/data_enc
, пожалуйста. - person Shepmaster   schedule 14.04.2019W
, я, конечно, могу сделатьW
частью подписи (encode(W, V)
), потому что кодировщики могут просто передать аргумент записи своему следующему кодировщику вместо того, чтобы использовать его. Это означает, что структурам кодировщика не нужно нести с собой модуль записи. Спасибо, @Laney и @Stargateur. - person Alexander Staubo   schedule 14.04.2019