【发布时间】:2019-09-04 09:33:13
【问题描述】:
我正在实现一个数据压缩接口:
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 特征以采用 writer 参数,该参数必须是 std::io::Write。这不起作用,因为某些编码器不会写入 std::io::Write,但是到另一个NumericEncoder。)
【问题讨论】:
-
为什么你的结构需要保存对文件的引用?当你调用 encode 时,为什么不直接给他们呢?
idEnc.encode(f, id);dataEnc.encode(f, data);这允许更大的灵活性。 -
“这不起作用,因为某些编码器不会写入 std::io::Write,而是写入另一个 NumericEncoder。”不清楚。这可能需要minimal reproducible example。
-
“这行不通,因为某些编码器不写入 std::io::Write,而是写入另一个 NumericEncoder” - 那么为什么不为
T: io::Write实现NumericEncoder呢?然后修改其签名以接受另一个NumericEncoder -
Idiomatic Rust 使用
snake_case表示变量、方法、宏、字段和模块;UpperCamelCase用于类型和枚举变体;SCREAMING_SNAKE_CASE用于静态和常量。请改用id_enc/data_enc。 -
这些问题让我意识到我没有考虑过签名。即使某些编码器写入另一个编码器,而不是
W,我当然可以将W作为签名的一部分(encode(W, V)),因为编码器可以只将 writer 参数传递给它的下一个编码器,而不是使用它.这意味着编码器结构不需要携带编写器。谢谢@Laney 和@Stargateur。
标签: rust mutability interior-mutability