TL;DR:我想要一个不可变元素的向量,它允许对其成员进行短暂的不可变引用,同时能够推送它。
我正在制作一个用于精确算术的板条箱,以用作浮点数的直接替代品。因此,它应该实施
Copy
。由于表达式是递归的,因此我查看了这篇博文,它将递归级别置于 Vec
中。
enum ExprData{
Val(u32),
Const{ch: char, f64: f64},
Sum(Vec<(u32, ExprRef)>)
}
#[derive(Clone, Copy)] // actually had to implement Clone myself,
struct FieldRef<'a> { // otherwise it'd clone the Field
index: usize, // or complain that Field is not Copy
field: &'a Field
}
#[derive(Clone, Copy)]
enum ExprRef<'a> {
Val(FieldRef<'a>)
Const(FieldRef<'a>)
Sum(FieldRef<'a>)
}
struct Field {
members: SpecialContainer<ExprData>,
}
impl Field {
fn parse(input: &str) {
assert_eq(input, "π+1")
self.maybe_add(ExprData::Const{char: 'π',f64: f64::consts::PI})
self.maybe_add(ExprData::Val(1))
self.maybe_add(ExprData::Sum(vec![(1,ExprRef::Const{FieldRef{field: &self, index: 0}}),(1,ExprRef::Val{FieldRef{field: &self, index: 1}})]))
ExprRef::Sum{FieldRef{field: &self, index: 2}}
}
fn maybe_add(data: ExprData) -> usize {
match self.members.position(data) { // <-- This requires equality, which is best with
Some(p) => p, // immutable references, cloning the vectors is
None => { // highly inefficient
self.members.push(data) // <-- THIS IS THE CULPRIT
self.members.len()-1
}
}
}
}
fn main () {
// Field: []
let pi_one: ExprRef = f.parse("π+1"); //
// Field: [π, 1, 1+π]
let two_pi_one = pi_one + pi_one;
// collect_like_terms([f.get_sum(pi_one),f.get_sum(pi_one)].concat())
// [1+π] + [1+π] -> [(1,1),(1,π),(1,1),(1,π)] -collect terms-> [(2,1),(2,π)]
// field.maybe_add(ExprData::Sum([(2,1),(2,π)])
// Field: [π, 1, 1+π, 2+2π]
}
让我重申一下:SpecialContainer 中的元素是不可变的,我只会push给它们。我想使用引用来避免克隆(可能很长)向量求和以求相等。
现在,我想到了以下选项:
Vec<OnceCell<ExprData>>
<- This doesn't allow for pushing, since pushing may move dataHashSet<ExprData>
<- this doesn't allow for inserting, not sure if it moves data if it exceeds capacity.RefCell<Vec<ExprData>>
<- This requires cloning of ExprData on every accessLinkedList<[OnceCell<ExprData>; CHUNK_SIZE]>
<- this would work, but LinkedList append still requires a mutable reference to the list and thus also to its members, even though pushing doesn't move the underlying data.AppendList<ExprData>
<- There's a lot of magic here, but it does what I wantCow
的东西?相似但不一样:这个
RTFD:
RefCell
实现map()
以将Ref
返回到数据的底层部分。既然你说:
让我重申一下:SpecialContainer 中的元素是不可变的,我只会推送它们。我想使用引用来避免克隆(可能很长)向量求和以求相等。
我假设您会在运行时检查您的使用情况。因此,如果需要可变性,返回
Ref<ExprData>
并可能使用 clone()
就是您想要的:
impl Field {
pub fn maybe_add(t: ExprData) -> usize {
let index: Option<usize> ;
// notice the scoping
{
index = self.members.borrow().iter().position(|a_t| *a_t == t)
}
match index {
Some(i) => i,
None => {
v.borrow_mut().push(t);
v.borrow().len()-1
}
}
}
pub fn get_data(&self, r: FieldRef) -> Ref<ExprData> {
// match statement omitted
Ref::map(self.members.borrow(), |v| v[r.index])
}
}
impl Eq for ExprRef {
fn eq(&self, other: &Self) {
// dereference to compare inner values
*self.field().get_data() == *other.field().get_data()
}
}
impl Add for ExprRef {
type Output = Self;
fn add(&self, rhs: Self) -> Self {
// do something smart with self.field().get_data().clone()
}
}
这个问题是与你所说的重复。