Serialization 如何在Rust中抽象远程或本地的可序列化对象?

Serialization 如何在Rust中抽象远程或本地的可序列化对象?,serialization,rust,enums,rpc,abstraction,Serialization,Rust,Enums,Rpc,Abstraction,我有一个枚举来表示本地(指当前节点)或远程的节点,在这种情况下,通过RPC调用进行通信: pub enum Node { Local, Remote(SocketAddr), } 我有一个表示本地数据库的structDB。我需要序列化这个节点enum,因为它被用作元数据来确定某些键值对存储的位置(本地或远程)。我将此元数据存储在与其余数据相同的数据库中,因此Node::Local引用了它存储在的数据库 我希望反序列化的节点枚举包含对从中取出的数据库的引用,以便我可以将Local

我有一个枚举来表示本地(指当前节点)或远程的节点,在这种情况下,通过RPC调用进行通信:

pub enum Node {
    Local,
    Remote(SocketAddr),
}
我有一个表示本地数据库的struct
DB
。我需要序列化这个
节点
enum,因为它被用作元数据来确定某些键值对存储的位置(本地或远程)。我将此元数据存储在与其余数据相同的数据库中,因此
Node::Local
引用了它存储在的数据库

我希望反序列化的节点枚举包含对从中取出的数据库的引用,以便我可以将
Local
变量转换为
Local(DB)
。然后,对于
Local
Remote
变体,我将使用相同的语义,因为它们都包含足够的信息来执行
Node.get(key)
Node.insert(key,value)
操作

对于远程变量,我只需打开到
SocketAddr
的连接,并发出RPC请求,但是对于
Local
变量,我必须在有本地数据库引用的地方进行模式匹配,并应用特殊逻辑,而不是一般地说
Node.get(key)
。基本问题是
Node::Local
中包含的信息不足以对本地数据库执行请求,而
Node::Remote
中的
SocketAddr
足以执行RPC调用


我可以通过创建一个自定义的反序列化方法来解决这个问题,在这个方法中,我传递了当前的
DB
作为对该反序列化方法的引用,但是我想知道是否还有其他好方法来解决这个问题。

我最终将这个区别表示为枚举。使用单独的枚举进行序列化和实际工作,因为我无法序列化对本地数据库的引用,如果不需要序列化,当然可以避开NodeRepr枚举:

#[derive(Serialize, Deserialize)]
pub enum NodeRepr {
    Local,
    Remote(NodeRef),
}

pub enum Node<'a> {
    Local(&'a LocalDB),
    Remote(NodeRef),
}

#[派生(序列化、反序列化)]
pub enum NodeRepr{
地方的
远程(NodeRef),
}
发布枚举节点选项{
匹配自我{
Node::Local(db)=>db.get(key),
Node::Remote(noderef)=>noderef.get(key)。等待,
}
}
}
这种处理方式允许我们仍然保留节点是本地还是远程的信息,以便在必要时在代码中的任何地方都可以使用这些信息。这是很有用的,因为您通常不希望总是隐藏某个东西是本地的还是远程的信息

例如,如果您总是想隐藏节点是远程的还是本地的以强制代码清洁,那么您可以使用trait对象,其中LocalDB和NodeRef都实现相同的trait


trait特别有用,因为它允许添加新的节点类型(本地和远程之外),而无需更改任何旧代码。对于enum,如果代码期望只有两个不同的enum情况,那么在添加新节点类型时,代码将中断。trait对象的局限性也可以通过应用经典的OOP模式(如visitor模式)来克服。

我最后将这种区别表示为枚举。使用单独的枚举进行序列化和实际工作,因为我无法序列化对本地数据库的引用,如果不需要序列化,当然可以避开NodeRepr枚举:

#[derive(Serialize, Deserialize)]
pub enum NodeRepr {
    Local,
    Remote(NodeRef),
}

pub enum Node<'a> {
    Local(&'a LocalDB),
    Remote(NodeRef),
}

#[派生(序列化、反序列化)]
pub enum NodeRepr{
地方的
远程(NodeRef),
}
发布枚举节点选项{
匹配自我{
Node::Local(db)=>db.get(key),
Node::Remote(noderef)=>noderef.get(key)。等待,
}
}
}
这种处理方式允许我们仍然保留节点是本地还是远程的信息,以便在必要时在代码中的任何地方都可以使用这些信息。这是很有用的,因为您通常不希望总是隐藏某个东西是本地的还是远程的信息

例如,如果您总是想隐藏节点是远程的还是本地的以强制代码清洁,那么您可以使用trait对象,其中LocalDB和NodeRef都实现相同的trait


trait特别有用,因为它允许添加新的节点类型(本地和远程之外),而无需更改任何旧代码。对于enum,如果代码期望只有两个不同的enum情况,那么在添加新节点类型时,代码将中断。trait对象的局限性也可以通过应用经典的OOP模式来克服,比如visitor模式。

trait如何?仍然不能解决序列化它的问题。即使我没有使用枚举,而是使用trait对象,这些trait对象仍然需要包含必要的信息,以便在反序列化时执行请求。对于SocketAddress,这不是问题,但我无法序列化整个数据库,因此,如果没有外部信息,local trait对象将无法执行db操作。听起来您将意识到,为什么本地和远程都不是可以抽象的小事。我在这里错过了一些信息。为什么Node::Local缺少信息?为什么不让它包含必要的信息?@Netwave OP不想提供一个最小的示例,所以我猜,但是它们在
DB
结构中有
节点。问题是
节点
需要的信息是包含
DB
本身的信息。这是一个经典的循环参考问题。它可以在Rust中解决,但循环引用在任何语言中都是一种痛苦。我鼓励你避免这样做。我可能会使用
DB::get(key)
调用
Node::get(key,local_DB)
来避免整个混乱局面。一个
特性如何
?这仍然不能解决问题