我正在编写一个MQTT5库。要发送数据包,在编写有效负载之前,我需要知道有效负载的大小。我确定大小的解决方案按重要性排序如下:
maintain
要确定大小,我可以执行以下任何一种解决方案:
如果不创建包装类型( annoying
std::iter::Chain本身的有效负载构建std::iter::ExactSizeIterator。我决定使用第3版。
下面的示例显示了我编写MQTT字符串迭代器的尝试。MQTT字符串由两个字节组成,这两个字节是字符串的长度,后面是数据作为utf8。
use std::iter::*;
use std::slice::Iter;
pub struct MQTTString<'a> {
chain: Chain<Iter<'a, u8>, Iter<'a, u8>>,
}
impl<'a> MQTTString<'a> {
pub fn new(s: &'a str) -> Self {
let u16_len = s.len() as u16;
let len_bytes = u16_len.to_be_bytes();
let len_iter = len_bytes.iter(); // len_bytes is borrowed here
let s_bytes = s.as_bytes();
let s_iter = s_bytes.iter();
let chain = len_iter.chain(s_iter);
MQTTString { chain }
}
}
impl<'a> Iterator for MQTTString<'a> {
type Item = &'a u8;
fn next(&mut self) -> Option<&'a u8> {
self.chain.next()
}
}
impl<'a> ExactSizeIterator for MQTTString<'a> {}
pub struct MQTTStringPait<'a> {
chain: Chain<std::slice::Iter<'a, u8>, std::slice::Iter<'a, u8>>,
}这个实现没有编译,因为我借用了len_bytes而不是移动它,所以在Chain使用它之前它会被删除:
error[E0515]: cannot return value referencing local variable `len_bytes`
--> src/lib.rs:19:9
|
12 | let len_iter = len_bytes.iter(); // len_bytes is borrowed here
| --------- `len_bytes` is borrowed here
...
19 | MQTTString { chain }
| ^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function有什么好办法吗?将len_bytes添加到MQTTString结构没有帮助。是否有更好的第四种办法来解决这个问题?
发布于 2021-01-15 16:37:12
根本问题是iter借用了数组。在夜间锈蚀中,可以使用array::IntoIter,但确实需要更改迭代器以返回u8而不是&u8
#![feature(array_value_iter)]
use std::array::IntoIter;
use std::iter::*;
use std::slice::Iter;
pub struct MQTTString<'a> {
chain: Chain<IntoIter<u8, 2_usize>, Copied<Iter<'a, u8>>>,
}
impl<'a> MQTTString<'a> {
pub fn new(s: &'a str) -> Self {
let u16_len = s.len() as u16;
let len_bytes = u16_len.to_be_bytes();
let len_iter = std::array::IntoIter::new(len_bytes);
let s_bytes = s.as_bytes();
let s_iter = s_bytes.iter().copied();
let chain = len_iter.chain(s_iter);
MQTTString { chain }
}
}
impl<'a> Iterator for MQTTString<'a> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
self.chain.next()
}
}
impl<'a> ExactSizeIterator for MQTTString<'a> {}通过使用Vec,您可以在“稳定锈蚀”中做同样的事情,但这可能有点过分。相反,由于您知道数组的确切大小,所以可以获取值并进行更多的链接:
use std::iter::{self, *};
use std::slice;
pub struct MQTTString<'a> {
chain: Chain<Chain<Once<u8>, Once<u8>>, Copied<slice::Iter<'a, u8>>>,
}
impl<'a> MQTTString<'a> {
pub fn new(s: &'a str) -> Self {
let u16_len = s.len() as u16;
let [a, b] = u16_len.to_be_bytes();
let s_bytes = s.as_bytes();
let s_iter = s_bytes.iter().copied();
let chain = iter::once(a).chain(iter::once(b)).chain(s_iter);
MQTTString { chain }
}
}
impl<'a> Iterator for MQTTString<'a> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
self.chain.next()
}
}
impl<'a> ExactSizeIterator for MQTTString<'a> {}另请参阅:
从纯效率的角度来看,&u8的迭代器不是一个好主意。在64位系统中,&u8占用64位,而u8本身占用8位。此外,在逐字节的基础上处理这些数据可能会阻碍围绕复制内存进行的常见优化。
相反,我建议创建一些可以将自身写入实现Write的东西。一个可能的执行:
use std::{
convert::TryFrom,
io::{self, Write},
};
pub struct MQTTString<'a>(&'a str);
impl MQTTString<'_> {
pub fn write_to(&self, mut w: impl Write) -> io::Result<()> {
let len = u16::try_from(self.0.len()).expect("length exceeded 16-bit");
let len = len.to_be_bytes();
w.write_all(&len)?;
w.write_all(self.0.as_bytes())?;
Ok(())
}
}另请参阅:
https://stackoverflow.com/questions/65739365
复制相似问题