Я реализую интерфейс сжатия данных:
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
.)