在 RefCell 后面的东西上实现借用?
Implement Borrow on something behind a RefCell?
我的项目中有结构 Value 和 RefValue。 RefValue 是一个引用计数的、动态可借用的 Value。现在 Value 可能包含 RefValue 的 HashMap,其中键和值都是 RefValue。
type ValueMap = HashMap<RefValue, RefValue>;
#[derive(Debug, PartialEq, Eq)]
enum Value {
Integer(i64),
String(String),
Map(ValueMap),
}
#[derive(Debug, PartialEq, Eq)]
struct RefValue {
value: Rc<RefCell<Value>>,
}
我自己在 RefValue 上实现了 Hash,在这个 playground.
中单独实现了一些 From-traits
我想要实现的是这样的主程序:
fn main() {
// Simple values
let x = RefValue::from(42);
let y = RefValue::from("Hello");
// Make a map from these values
let mut z = ValueMap::new();
z.insert(RefValue::from("x"), x);
z.insert(RefValue::from("y"), y);
// Make a value from the map
let z = RefValue::from(z);
println!("z = {:?}", z);
// Try to access "x"
if let Value::Map(m) = &*z.borrow() {
println!("m[x] = {:?}", m["x"]); // <- here I want to access by &str
};
}
不幸的是,我得到了奇怪的结果,您可以在 playground 评论中找到。我也很不确定是否没有更好的方法来解决整个问题,因为 RefCell 不能 return 其包含元素的借用值。
有人可以给我提示吗?
当您实施 Borrow<T>
、your Hash
implementation must return the same hash value as T
's for when the underlying value is equivalent 时。即如果x.hash()
一定等于x.borrow().hash()
。 HashMap
在索引时依赖于此 属性:它需要 Idx: Borrow<Key>
然后使用此规则来确保它可以找到该值。
您的 impl Borrow<str> for RefValue
不遵守此规则。 RefValue::hash()
for RefValue::String
在散列字符串之前调用 write_u8(2)
。因为你违反了合同,hashmap 可以做任何事情(不包括未定义的行为),比如恐慌、中止进程或找不到你的密钥,这就是它在这种情况下所做的。
要解决这个问题,您不应该散列判别式(为了保持一致性,也将其从其他判别式中删除):
impl Hash for RefValue {
fn hash<H: Hasher>(&self, state: &mut H) {
match &*self.borrow() {
Value::Integer(i) => {
i.hash(state);
}
Value::String(s) => {
s.hash(state);
}
Value::Map(m) => {
(m as *const ValueMap as usize).hash(state); // Object address
}
}
}
}
现在它在您的 Borrow
实施中出现恐慌,正如您预期的那样 (playground)。
但你不应该实施 Borrow
,因为实施它意味着你的价值是借来价值的反映。 RefValue
绝不是 str
。它也可以是整数或映射。因此,您不应该为其中任何一个实现 Borrow
。您可以实施 Borrow<Value>
,但这是不可能的,因为您使用 RefCell
因此需要 return Ref
但 Borrow
要求 return 引用.你倒霉了。您唯一的选择是使用 RefValue
s.
进行索引
最后,您应该避免键的内部可变性。一旦改了,很容易误改,你的hash/equality改,又一次违约
我的项目中有结构 Value 和 RefValue。 RefValue 是一个引用计数的、动态可借用的 Value。现在 Value 可能包含 RefValue 的 HashMap,其中键和值都是 RefValue。
type ValueMap = HashMap<RefValue, RefValue>;
#[derive(Debug, PartialEq, Eq)]
enum Value {
Integer(i64),
String(String),
Map(ValueMap),
}
#[derive(Debug, PartialEq, Eq)]
struct RefValue {
value: Rc<RefCell<Value>>,
}
我自己在 RefValue 上实现了 Hash,在这个 playground.
中单独实现了一些 From-traits我想要实现的是这样的主程序:
fn main() {
// Simple values
let x = RefValue::from(42);
let y = RefValue::from("Hello");
// Make a map from these values
let mut z = ValueMap::new();
z.insert(RefValue::from("x"), x);
z.insert(RefValue::from("y"), y);
// Make a value from the map
let z = RefValue::from(z);
println!("z = {:?}", z);
// Try to access "x"
if let Value::Map(m) = &*z.borrow() {
println!("m[x] = {:?}", m["x"]); // <- here I want to access by &str
};
}
不幸的是,我得到了奇怪的结果,您可以在 playground 评论中找到。我也很不确定是否没有更好的方法来解决整个问题,因为 RefCell 不能 return 其包含元素的借用值。
有人可以给我提示吗?
当您实施 Borrow<T>
、your Hash
implementation must return the same hash value as T
's for when the underlying value is equivalent 时。即如果x.hash()
一定等于x.borrow().hash()
。 HashMap
在索引时依赖于此 属性:它需要 Idx: Borrow<Key>
然后使用此规则来确保它可以找到该值。
您的 impl Borrow<str> for RefValue
不遵守此规则。 RefValue::hash()
for RefValue::String
在散列字符串之前调用 write_u8(2)
。因为你违反了合同,hashmap 可以做任何事情(不包括未定义的行为),比如恐慌、中止进程或找不到你的密钥,这就是它在这种情况下所做的。
要解决这个问题,您不应该散列判别式(为了保持一致性,也将其从其他判别式中删除):
impl Hash for RefValue {
fn hash<H: Hasher>(&self, state: &mut H) {
match &*self.borrow() {
Value::Integer(i) => {
i.hash(state);
}
Value::String(s) => {
s.hash(state);
}
Value::Map(m) => {
(m as *const ValueMap as usize).hash(state); // Object address
}
}
}
}
现在它在您的 Borrow
实施中出现恐慌,正如您预期的那样 (playground)。
但你不应该实施 Borrow
,因为实施它意味着你的价值是借来价值的反映。 RefValue
绝不是 str
。它也可以是整数或映射。因此,您不应该为其中任何一个实现 Borrow
。您可以实施 Borrow<Value>
,但这是不可能的,因为您使用 RefCell
因此需要 return Ref
但 Borrow
要求 return 引用.你倒霉了。您唯一的选择是使用 RefValue
s.
最后,您应该避免键的内部可变性。一旦改了,很容易误改,你的hash/equality改,又一次违约