在泛型中将特征约束作为类型使用
我在为一个需要根据消息把消息发送给正确目标实体的实体实现一个带有trait约束的泛型参数时遇到了困难。设想有一个名为MessageTrait的 trait和 MessageSender:
use serde::Serialize;
use std::collections::HashMap;
pub trait MessageTrait: Serialize + Clone + PartialEq {} // trait to allow concrete message (e.g. Message2) to be substituted in MessageSender
pub trait MessageSender<Message: MessageTrait> {
fn send_message(&mut self, message: &Message);
}
以及消息message1..messageN(仅展示Messag1):
// messages to send
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct Message1 {}
impl MessageTrait for Message1 {}
以及使用UDP发送MessageX的面向特定协议的MessageSender:
// UDP send
pub struct UdpSender {
// UpdSender fields
}
impl MessageSender<Message1> for UdpSender {
fn send_message(&mut self, message: &Message1) {
let send_msg = message.clone();
// ... do stuff to send data using UDP
}
}
// impl MessageSender<Message2> for UdpSender { ... }
最后,使用以String为键的HashMap,其中包含MessageSender,以便找到正确的MessageSender:
// fix MessageRouter to allow different MessageSender types (i.e. something other than UdpSender)
struct MessageRouter {
maps: HashMap<String, UdpSender>,
}
impl MessageRouter {
pub fn new() -> Self {
Self {
maps: HashMap::new(), // or initialize maps here ...
}
}
pub fn send_msg(&mut self, id: &str, message: &Message1) {
// ...
if let Some(sender) = self.maps.get_mut(id) {
// find sender in map
sender.send_message(message)
}
}
}
上面的代码可以编译,但我想让MessageRouter接受一个泛型类型,这样就可以替换除UdpSender之外的实现(例如TCP/Unix套接字或测试用的Stub):
struct MessageRouter<T> {
maps: HashMap<String, T>, // error
}
但下面的代码会报错:expected a type, found a trait
impl<T> MessageRouter<T> where T: MessageSender<dyn MessageTrait> { // error
pub fn new() -> Self {
Self {
maps: HashMap::new(),
}
}
pub fn send_msg(&mut self, id: &str, msg: &T) {
// ...
if let Some(sender) = maps.get_mut(id) {
sender.send_message(msg);
..
}
}
}
也尝试过 MessageSender<Box<dyn MessageTrait>>,但这同样不起作用。如何将T 定义为实现MessageSender trait的类型,以便在MessageRouter中将其用作HashMap
解决方案
一个类型的泛型(HashMap)是一个类型,但 MessageSender 是一个trait。
你可以通过使用 Box<dyn MessageSender> 来实现,Box 是一个把数据移动到堆上的唯一指针,在这种情况下,结合 dyn,它只是把数据当作一个 MessageSender 接口来对待,这样在sender路由下的所有发送者就共享同一个类型。
在问题中,你提到有多种消息数据类型,然而,MessageRouter<T>,意味着 MessageRouter 只能接收一种数据类型,除非把消息包装在 Box 中,否则你不能用 send_message 发送多种数据类型。因此你也许还需要重新考虑你的 MessageRouter 结构。
然而,在这种设置下,你可以把同一消息类型发送给多个发送者,如下所示。
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub trait MessageTrait: Serialize + Clone + PartialEq {}
pub trait MessageSender<Message: MessageTrait> {
fn send_message(&mut self, message: &Message);
}
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct Message1 {}
impl MessageTrait for Message1 {}
#[derive(Debug, Clone, Serialize, PartialEq)]
// UDP send
pub struct UdpSender {}
pub struct UdpSender2 {}
impl MessageSender<Message1> for UdpSender {
fn send_message(&mut self, message: &Message1) {
println!("udp 1 sending 1");
}
}
impl MessageSender<Message1> for UdpSender2 {
fn send_message(&mut self, message: &Message1) {
println!("udp 2 sending 1");
}
}
struct MessageRouter<T: MessageTrait> {
maps: HashMap<String, Box<dyn MessageSender<T>>>,
}
impl<T: MessageTrait> MessageSender<T> for MessageRouter<T> {
fn send_message(&mut self, message: &T) {
println!("sending message from MessageRouter . . .");
for (r, v) in self.maps.iter_mut() {
println!("delegating route : {}", r);
v.send_message(message);
}
}
}
fn main() {
let mut message_router: MessageRouter<Message1> = MessageRouter {
maps: [
(
"router".to_string(),
Box::new(UdpSender {}) as Box<dyn MessageSender<Message1>>,
),
(
"second".to_string(),
Box::new(UdpSender2 {}) as Box<dyn MessageSender<Message1>>,
),
]
.into_iter()
.collect(),
};
message_router.send_message(&Message1 {});
// sending message from MessageRouter . . .
// delegating route : router
// udp 1 sending 1
// delegating route : second
// udp 2 sending 1
}