[英]Why doesn't bincode detect an error if I deserialize data into the wrong type?
當我嘗試將二進制數據反序列化為錯誤類型時,為什么我不會從 bincode 中收到錯誤消息?
use bincode; // 1.3.1
use serde::{Deserialize, Serialize}; // { version = "1.0", features = ["derive"] }
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
pub struct Ping {
pub pinger_id: usize,
pub useless_field: usize,
pub i_need_one_more: usize,
}
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
pub struct Heartbeat {
pub term: usize,
pub node_id: usize,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum Message {
Heartbeat(Heartbeat),
Ping(Ping),
}
fn main() {
let rpc_message_bin = bincode::serialize(&Ping {
pinger_id: 0,
useless_field: 1,
i_need_one_more: 2,
})
.unwrap();
let m: Message = bincode::deserialize(&rpc_message_bin).unwrap();
println!("{:#?}", m);
}
我期待收到Message::Ping
但我得到:
Heartbeat (
Heartbeat {
term: 4294967296,
node_id: 8589934592,
},
)
bincode 信任用戶反序列化為預期的類型,你正在做的事情有“隨機”的結果,它是安全的,但它是實現行為。
下面只是一個例子,這可能是錯誤的,但邏輯是正確的。 enum
in rust are implementation behavior, bincode is "abusing" rust by assuming an enum
is always represented with a unsigned integer value, bincode also choice to encode it as u32
value "enums variants are encoded as a usize
u32
u32
is足以滿足所有實際用途。” . 從用戶的角度來看,這並不重要(除了最大2**32
變體的enum
的“限制”......)。
所以,這就是 bincode 的做法。 在您的代碼中,您要求 bincode 重新加入Ping
結構,而不是變體Message::Ping
。
這意味着編碼緩沖區將包含3
個類似Ping
結構的usize
。 然后您要求 bincode 將此數據解釋為Message
enum
,基本上這將要求u32
從緩沖區中讀取 u32 ,在此示例中,這將通過讀取0
得到,而這恰好是數字 rust 和 bincode 用於表示Message
enum
的第一個變體。 所以 bincode 會認為“好的,我正在閱讀Message::Heartbeat
然后 bincode 將再讀取 2 個 usize 以填充Heartbeat
結構。就像在 64 位系統中讀取 u32 會引入4
個八位字節的偏移量,bincode 不會讀取1
和2
但1 << 32
和2 << 32
。
這意味着在編碼緩沖區中你有類似的東西
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]
^ first usize $ ^ second usize $ ^ last usize $
^ u32 $ ^ first usize $ ^ second usize $
從 bincode 的角度來看,這是完全有效的。 bincode 意味着要與閱讀器一起使用,因此閱讀器 cursor 仍有4
個八位字節要讀取。
我們可以玩一下,如果你稍微改變一下編碼值pinger_id: usize::MAX
,你會得到一個錯誤信息:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("invalid value: integer `4294967295`, expected variant index 0 <= i < 2")', src\main.rs:31:61
我們還可以通過將第一個使用大小從Ping
更改為usize
u32
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
pub struct Ping {
pub pinger_id: u32,
pub useless_field: usize,
pub i_need_one_more: usize,
}
現在使用這些值進行編碼:
let rpc_message_bin = bincode::serialize(&Ping {
pinger_id: 0,
useless_field: 1,
i_need_one_more: 2,
})
將導致擁有1
和2
:
Heartbeat(
Heartbeat {
term: 1,
node_id: 2,
},
)
如果Ping
結構太小:
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
pub struct Ping {
pub pinger_id: usize,
}
bincode 會出錯,說缺少數據:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Io(Kind(UnexpectedEof))', src\main.rs:27:61
因此,總而言之,如果將變體反序列化為枚舉類型,則不得發送變體的“直接”值。 使用 bincode 或任何序列化工具時,您必須始終將您編碼的類型與您解碼的類型匹配,因此您必須直接序列化Message::Ping(Ping{.. })
而不是Ping {.. }
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.