Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Rust 如何修改哈希集中不属于哈希计算的属性?_Rust_Hashset - Fatal编程技术网

Rust 如何修改哈希集中不属于哈希计算的属性?

Rust 如何修改哈希集中不属于哈希计算的属性?,rust,hashset,Rust,Hashset,我有一个包含唯一id的结构,并将该id用于其哈希: use std::borrow::Borrow; use std::collections::HashSet; use std::hash::{Hash, Hasher}; type Id = u32; #[derive(Debug, Eq)] struct Foo { id: Id, other_data: u32, } impl PartialEq for Foo { fn eq(&self, othe

我有一个包含唯一id的结构,并将该id用于其哈希:

use std::borrow::Borrow;
use std::collections::HashSet;
use std::hash::{Hash, Hasher};

type Id = u32;

#[derive(Debug, Eq)]
struct Foo {
    id: Id,
    other_data: u32,
}

impl PartialEq for Foo {
    fn eq(&self, other: &Foo) -> bool {
        self.id == other.id
    }
}

impl Hash for Foo {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

impl Borrow<Id> for Foo {
    fn borrow(&self) -> &Id {
        &self.id
    }
}
这是一种反模式吗;我应该使用HashMap吗


这在您当前的数据结构中是不可能的

HashSet故意不提供改变值的方法。正如您所提到的,在大多数情况下,改变哈希集中的值或哈希映射中的键将使哈希无效。API鼓励正确使用,甚至提到:

如果一个项被修改为该项的哈希值(由trait决定)或其相等值(由trait决定)在集合中时发生变化,则这是一个逻辑错误。这通常只能通过全局状态、I/O或不安全代码实现

这暗示了一种解决问题的方法,即使用内部可变性:

use std::cell::Cell;

#[derive(Debug, Eq)]
struct Foo {
    id: Id,
    other_data: Cell<u32>,
}
这是一个合理的做法,但我不会为此感到兴奋。相反,我将允许将我的类型分解为一个键和一个值,并将其存储在HashMap中,如前所述。差不多


impl Foo {
    // or insert_into_hashmap(self, &mut HashMap<Id, u32>)
    fn into_key_value(self) -> (Id, u32) {
        (self.id, self.other_data)
    }

    // Maybe a
    //
    // fn from_key_value(&'a Id, &'a u32) -> Self
    // or
    // fn from_hashmap(Id, &HashMap<Id, u32>) -> Self
}

// Maybe a
//
// struct FooRef<'a> { (or FooRefMut?) 
//     id: &'a Id,
//     other_data: &'a u32,
// }
//
// With a
// fn from_key_value(&'a Id, &'a u32) -> Self
// or
// fn from_hashmap(Id, &HashMap<Id, u32>) -> Self

fn main() {
    let mut baz = HashMap::new();
    let f = Foo {
        id: 1,
        other_data: 2,
    };
    let (k, v) = f.into_key_value();
    baz.insert(k, v);

    // See also HashMap::get_key_value
    if let Some(v) = baz.get_mut(&1) {
        *v = 3;
    }
}

这在您当前的数据结构中是不可能的

HashSet故意不提供改变值的方法。正如您所提到的,在大多数情况下,改变哈希集中的值或哈希映射中的键将使哈希无效。API鼓励正确使用,甚至提到:

如果一个项被修改为该项的哈希值(由trait决定)或其相等值(由trait决定)在集合中时发生变化,则这是一个逻辑错误。这通常只能通过全局状态、I/O或不安全代码实现

这暗示了一种解决问题的方法,即使用内部可变性:

use std::cell::Cell;

#[derive(Debug, Eq)]
struct Foo {
    id: Id,
    other_data: Cell<u32>,
}
这是一个合理的做法,但我不会为此感到兴奋。相反,我将允许将我的类型分解为一个键和一个值,并将其存储在HashMap中,如前所述。差不多


impl Foo {
    // or insert_into_hashmap(self, &mut HashMap<Id, u32>)
    fn into_key_value(self) -> (Id, u32) {
        (self.id, self.other_data)
    }

    // Maybe a
    //
    // fn from_key_value(&'a Id, &'a u32) -> Self
    // or
    // fn from_hashmap(Id, &HashMap<Id, u32>) -> Self
}

// Maybe a
//
// struct FooRef<'a> { (or FooRefMut?) 
//     id: &'a Id,
//     other_data: &'a u32,
// }
//
// With a
// fn from_key_value(&'a Id, &'a u32) -> Self
// or
// fn from_hashmap(Id, &HashMap<Id, u32>) -> Self

fn main() {
    let mut baz = HashMap::new();
    let f = Foo {
        id: 1,
        other_data: 2,
    };
    let (k, v) = f.into_key_value();
    baz.insert(k, v);

    // See also HashMap::get_key_value
    if let Some(v) = baz.get_mut(&1) {
        *v = 3;
    }
}
我相信在这种情况下,不安全代码是最好的途径

impl Foo {
    fn set_other_data(set: &mut HashSet<Foo>, id: &Id, data: u32) -> bool{
        match set.get(id) {
            Some(x) => {
                let p: *const Foo = x;
                let q: *mut Foo = p as *mut Foo;
                unsafe {
                    (*q).other_data = data;
                }
                return true;
            }
            None => return false,
        }
    }
}

fn main() {
    let mut baz = HashSet::new();
    baz.insert(Foo {
        id: 1,
        other_data: 2,
    });

    Foo::set_other_data(&mut baz, &1, 3);
    assert_eq!(3, baz.get(&1).unwrap().other_data);
}
正如谢普马斯特所引述:

如果一个项被修改为该项的哈希值(由trait决定)或其相等值(由trait决定)在集合中时发生变化,则这是一个逻辑错误。这通常只能通过全局状态、I/O或不安全代码实现

在这种情况下,哈希或Eq特征不使用其他_数据。所以它可以安全地变异。最大的危险是,在以后的某个时刻,Foo的Hash或Foo的Eq将被修改,并包含其他的_数据

不存在数据争用的危险,因为哈希集是可变借用的

其他选择:

分解:当Foo只有2个元素,但假设Foo包含许多元素时,这一点就起作用了。您是将Foo分解为其所有单独的元素,还是在Foo代码膨胀中创建子结构

封装:Silvio Mayolo建议将Foo封装在内部使用HashMap的类似HashSet的接口中。这使得API保持干净,只使用安全代码,但似乎需要更多的编程

我将非常感谢您的反馈,如果这看起来合理,我可以为HashSet的不安全fn get_mut提交一个功能请求。

我认为不安全代码是这种情况下的最佳途径

impl Foo {
    fn set_other_data(set: &mut HashSet<Foo>, id: &Id, data: u32) -> bool{
        match set.get(id) {
            Some(x) => {
                let p: *const Foo = x;
                let q: *mut Foo = p as *mut Foo;
                unsafe {
                    (*q).other_data = data;
                }
                return true;
            }
            None => return false,
        }
    }
}

fn main() {
    let mut baz = HashSet::new();
    baz.insert(Foo {
        id: 1,
        other_data: 2,
    });

    Foo::set_other_data(&mut baz, &1, 3);
    assert_eq!(3, baz.get(&1).unwrap().other_data);
}
正如谢普马斯特所引述:

如果一个项被修改为该项的哈希值(由trait决定)或其相等值(由trait决定)在集合中时发生变化,则这是一个逻辑错误。这通常只能通过全局状态、I/O或不安全代码实现

在这种情况下,哈希或Eq特征不使用其他_数据。所以它可以安全地变异。最大的危险是,在以后的某个时刻,Foo的Hash或Foo的Eq将被修改,并包含其他的_数据

不存在数据争用的危险,因为哈希集是可变借用的

其他选择:

分解:当Foo只有2个元素,但假设Foo包含许多元素时,这一点就起作用了。您是将Foo分解为其所有单独的元素,还是在Foo代码膨胀中创建子结构

封装:Silvio Mayolo建议将Foo封装在内部使用HashMap的类似HashSet的接口中。这使得API保持干净,只使用安全代码,但似乎需要更多的编程


非常感谢您的反馈,如果这看起来合理,我可以为HashSet的不安全fn get_mut提交一个功能请求。

或者这是一个反模式,我应该使用HashMap吗?我想你说得对极了。您需要一个具有不可变哈希部分和可变非哈希部分的数据结构,这正是HashMap提供给您的。@SilvioMayolo My structs包含其唯一ID。对于HashMap,如何确保键与结构包含的id值相对应?我建议将其封装在一个hashset中,如interf
王牌您可以在内部使用hashmap并封装关于ID的保证。或者这是一种反模式,我应该使用hashmap吗?我想你说得对极了。您需要一个具有不可变哈希部分和可变非哈希部分的数据结构,这正是HashMap提供给您的。@SilvioMayolo My structs包含其唯一ID。对于HashMap,如何确保键与结构包含的id值相对应?我建议将其封装在类似hashset的接口中。您可以在内部使用hashmap并封装关于ID的保证。我相信这段代码会导致未定义的行为,因此不能推荐它。这是一个Miri检测违规行为的地方。不,你不允许这样做,你不能改变应该设置为常量的东西。getid@ShepmasterMiri抱怨错误[E0080]:常量求值错误:正在访问的借用AliasNone在借用堆栈上不存在。但是,在我上面给出的代码中,不必担心任何其他线程会改变HashSet,因为HashSet是可变借用的:set:&mut HashSet。我不知道未定义的行为是什么。@HideFinder线程与未定义的行为无关。Rust编译器可以假设没有任何东西会改变不可变引用的引用值,除非有:一般来说,将&T类型转换为&mut T被认为是未定义的行为。我相信这段代码会导致未定义的行为,因此不能推荐。这是一个Miri检测违规行为的地方。不,你不允许这样做,你不能改变应该设置为常量的东西。getid@ShepmasterMiri抱怨错误[E0080]:常量求值错误:正在访问的借用AliasNone在借用堆栈上不存在。但是,在我上面给出的代码中,不必担心任何其他线程会改变HashSet,因为HashSet是可变借用的:set:&mut HashSet。我不知道未定义的行为是什么。@HideFinder线程与未定义的行为无关。Rust编译器可以假定不可变引用的引用值不会发生任何变化,除非存在:一般来说,将&T类型转换为&mut T被认为是未定义的行为。