新人小白请教各位大佬一个问题,
我想在socket中实现一个可以用于各种类型交互的送信收信的函数,送信函数接受一个范型参数x,进行序列化并且发送给对方。收信端收到之后进行反序列化然后返回这个x。
但是我不知道怎么匹配参数x的类型?
这是我的代码:
#[derive(serde::Serialize, serde::Deserialize, Debug)]
enum Message {
I32(i32),
Bool(bool),
String(String)
}
unsafe fn write<A>(socket, x: A)
where A: marker::Send + 'static + serde::Serialize
{
let buffer = match A{ //请教一下这部分怎么实现?或者有没有其他的方案?
i32 =>{ bincode::serialize(&Message::I32(x)).unwrap();},
bool=> {......},
String=>{......},
}
let mut stream = BufWriter::new(socket);
Write::write_all(&mut stream,&buffer).unwrap();
Write::flush(&mut stream).unwrap();
println!("send buffer: {:?}", buffer);
}
unsafe fn read<A>(socket) -> A
where A: marker::Send + 'static
{
let mut buffer:Vec<u8> = [0u8;4].to_vec();
let mut stream = BufReader::new(socket);
let a =Read::read(&mut stream ,&mut buffer).unwrap();
let message = bincode::deserialize(&mut buffer[..]).unwrap();
let result = match message { //还有这里
Message::I32(int) => ...A,
//Message::Bool(bo) => ...A,
//Message::String(str)=> ...A,
_ => panic!("unsupported message type")
};
result
}
想匹配类型的原因是如果序列化的时候没有标注x的具体类型,那么反序列化的时候就会提示出错:
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("invalid value: integer `23`, expected variant index 0 <= i < 2")',
读不太懂这个错误提示,但是我如果序列化的时候是直接声明Message::I32(x),那么就不会报错,但是要求是传递到write函数的参数是范型A,并且read函数的返回值也要求是范型A,所以就想着要匹配一下参数x的类型然后分情况序列化?
感谢各位提供建议和想法~
问题已经解决如下:
unsafe fn write<A>(socket, x: A)
where A: marker::Send + 'static + serde::Serialize
{
let mut stream = BufWriter::new(socket);
Write::write_all(&mut stream,&buffer).unwrap();
Write::flush(&mut stream).unwrap();
println!("send buffer: {:?}", buffer);
}
unsafe fn read<A>(socket) -> A
where A: marker::Send + 'static + DeserializeOwned
{
let mut buffer:Vec<u8> = [0u8;1024].to_vec();
let mut stream = BufReader::new(socket);
let a =Read::read(&mut stream ,&mut buffer).unwrap();
let message:A = bincode::deserialize(&mut buffer[..]).unwrap();
message
}
只需要对范型A添加DeserializeOwned trait。
1
共 5 条评论, 1 页
评论区
写评论是的这样也可以,但是我找到了新的更合适的方法,谢谢你的建议~
--
👇
ywxt: 如果Message只有三种情况,那直接传入Message不就行了。
非常感谢大佬不厌其烦地解释。受益匪浅。
我后来仔细想了想,我发现其实是我想法走偏了,枚举方法是对的,但是对我来说还是比较麻烦,我看了serde bincode文档之后发现一个新方法。
其实在反序列化的时候,将范型A加上DeserializeOwned trait,就可以直接实现对于范型的反序列化了。
这样就不必通过枚举来处理范型A了。
感谢你提供了重要思路,因为我也是刚开始学Rust,所以这些概念没掌握清楚,惭愧惭愧!我也会把枚举方法在实现一遍,加深一下理解。
总之非常感谢你~祝你工作学习顺利~
贴一下我解决后的代码:
还有一些小问题总结: 反序列化的时候,buffer的大小是不一样的,例如整数是4byte,但是string就是22,所以直接往大了整就行。我把buffer设置为 Vec::new() 之后会报错:
我尽量往大了设然后就不会了,我觉得可能是一开始设一个空的Vector会导致读不进数据?
还有就是在我这个例子里不能使用read_to_end()函数,要使用read()。
--
👇
ruby: 简单来说,传输层就是
enum Message
的实例序列化成字节流的格式你这写法真是没理解enum
bincode::serialize(&Message::I32(x))
发送端示例:
let request: Vec<u8> = bincode::serialize::<Message>(&Message::I32(66));
接收端示例:
let request: Message = bincode::deserialize::<Message>(bytes);
你如果觉得我的示例过于简陋,你可以学习下actix-web官方代码是怎样用enum+match将websocket通信中7种不同消息类型进行解析,并针对不同类型的消息做不同处理
https://github.com/actix/examples/blob/e5ef02da965519f863511c46adee110b0e9f34aa/websockets/websocket/src/main.rs#L53
以下教科书一般的对WebSocket各种消息类型进行模式匹配的例子抄自actix-web源码
简单来说,传输层就是
enum Message
的实例序列化成字节流的格式你这写法真是没理解enum
bincode::serialize(&Message::I32(x))
发送端示例:
let request: Vec<u8> = bincode::serialize::<Message>(&Message::I32(66));
接收端示例:
let request: Message = bincode::deserialize::<Message>(bytes);
你如果觉得我的示例过于简陋,你可以学习下actix-web官方代码是怎样用enum+match将websocket通信中7种不同消息类型进行解析,并针对不同类型的消息做不同处理
https://github.com/actix/examples/blob/e5ef02da965519f863511c46adee110b0e9f34aa/websockets/websocket/src/main.rs#L53
以下教科书一般的对WebSocket各种消息类型进行模式匹配的例子抄自actix-web源码
我在上个帖子都已经给你能运行的示例代码了,肯定是把整个enum Message的实例对象进行序列化传输,你再认真研究下我代码。话说你这一堆unsafe看着没必要,建议看看张老师极客时间的Rust课程,里面都讲了enum是类型构造器,类似C语言tagged Union,你把enum乱用成这样真应该把Rust官方英文的refernce中的enum从头读几遍。
如果Message只有三种情况,那直接传入Message不就行了。