Имеется массив u8 (принятый по TCP), нужно получить строковый срез этого массива. Массив и &str желательно держать в одной структуре.
То есть должно получиться что-то типа СИшного union’a - возможность чтения существующего массива как строкового среза без выделения дополнительных векторов, String и т.д.
Проблемы с временами жизни.
Можно ли как-нибудь это сделать не используя глобальную переменную static?
Как понять, какое время жизни имеет массив input_buf в структуре (видимо не 'static)?
use std::str;
use std::io::Write;
use std::io::Read;
use std::net::TcpListener;
pub struct Packet <'a> //Тут 'a, чтобы сделать src_packet не 'static
{
pub input_buf: [u8; 512], //Какое время жизни у этого массива?
pub src_packet: Option<&'a str>,
}
impl Packet <'static> //Тут просит указать время жизни. Какое указывать?
{
///Конструктор
fn new() -> Packet <'static> //Тут просит указать время жизни. Какое указывать?
{
Packet
{
input_buf: [0u8; 512],
src_packet: None,
}
}
fn extract_pkt(&self)
{
self.src_packet = Some( str::from_utf8(&self.input_buf).unwrap() ); //Создание среза &str
}
}
fn main()
{
let mut pkt = Packet::new();
let addr = "127.0.0.1:9999";
let listener = TcpListener::bind(addr).unwrap();
println!("Server listening at {}", addr);
for stream in listener.incoming() //stream типа TcpStream
{
let mut stream = stream.unwrap();
stream.read(&mut pkt.input_buf);
pkt.extract_pkt(); //Вызов метода
}
}
Build:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src\main.rs:29:48
|
29 | self.src_packet = Some( str::from_utf8(&self.input_buf).unwrap() );
| ^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 28:4...
--> src\main.rs:28:5
|
28 | / {
29 | | self.src_packet = Some( str::from_utf8(&self.input_buf).unwrap() );
30 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src\main.rs:29:48
|
29 | self.src_packet = Some( str::from_utf8(&self.input_buf).unwrap() );
| ^^^^^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected std::option::Option<&'static str>, found std::option::Option<&str>)
--> src\main.rs:29:27
|
29 | self.src_packet = Some( str::from_utf8(&self.input_buf).unwrap() );
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
То есть, насколько я понял что-то не так со временем жизни input_buf.
Не соответствуют времена жизни массива input_buf и среза src_packet, При этом, я не понимаю какое время у input_buf.
Или проблема со временем жизни всей структуры?
Помогите пожалуйста разобраться, уже кучу вариантов перепробовал.
Думаю тебе не нужно хранить &str в структуре str::from_utf8 не выделяет память под строку, а просто возвращает ссылку на эти же байты в виде строки, если это валидный utf8.
use std::io::Read;
use std::io::Write;
use std::net::TcpListener;
use std::str;
pub struct Packet<'a> {
pub input_buf: &'a mut [u8],
}
impl<'a> Packet<'a> {
fn new(input_buf: &mut [u8]) -> Packet<'_> {
Packet { input_buf }
}
fn extract_pkt(&self) -> Option<&str> {
str::from_utf8(self.input_buf).ok()
}
}
fn main() {
let mut buf = vec![0; 512];
let pkt = Packet::new(&mut buf);
let addr = "127.0.0.1:9999";
let listener = TcpListener::bind(addr).unwrap();
println!("Server listening at {}", addr);
for stream in listener.incoming()
{
let mut stream = stream.unwrap();
stream.read(pkt.input_buf).unwrap();
pkt.extract_pkt();
}
}
kpp, спасибо за информацию. vesss, спасибо за информацию и пример.
vessd, я понимаю, что str::from_utf8(…) не выделяет память, а возвращает &str, поэтому его и использую.
У меня тоже была идея не хранить в стуктуре &str, а возвращать через метод. Но дело в том, что на самом деле мне нужен срез &str не всего буфера input_buf, а только содержимого самого принятого пакета, кроме того нужны &str на определенные данные (куски) внутри этого пакета. Пакет внутри буфера input_buf может быть принят любой длины. Мне бы хотелось сформировать все эти &str за раз в одном методе (который будет вызван в начале программы), и не тратить время на парсинг пакета в середине программы.
Не хотел использовать static-переменную, но ничего другого не придумал.
Так не слишком отстойно будет?
И все-таки, для общего понимания, какое время жизни у массива input_buf в моей первой реализации?
use std::str;
use std::io::Write;
use std::io::Read;
use std::net::TcpListener;
pub static mut INPUT_BUF: [u8; 512] = [0u8; 512];
pub struct Packet
{
len: Option<usize>, //Длина пакета в буфере
pub src_packet: Option<&'static str>, //&str на пакет
pub cs: Option<&'static str>,
}
impl Packet
{
///Конструктор
pub fn new() -> Packet
{
Packet
{
len: None,
src_packet: None,
cs: None,
}
}
///Переинициализация
unsafe fn reset() -> Packet
{
INPUT_BUF = [0u8; 512];
Packet
{
len: None,
src_packet: None,
cs: None,
}
}
unsafe fn extract_pkt(& mut self)
{
self.src_packet = str::from_utf8(&INPUT_BUF[0..self.len.unwrap()]).ok();
self.cs = str::from_utf8(&INPUT_BUF[self.len.unwrap()-2..self.len.unwrap()]).ok();
}
}
fn main()
{
let mut pkt = Packet::new();
let addr = "127.0.0.1:9999";
let listener = TcpListener::bind(addr).unwrap();
println!("Server listening at {}", addr);
for stream in listener.incoming() //stream типа TcpStream
{
let mut stream = stream.unwrap();
loop
{
unsafe{
pkt = Packet::reset();
pkt.len = stream.read(&mut INPUT_BUF).ok();
pkt.extract_pkt();
};
//Работа с данными из пакета...
}
}
}
То что вы хотите можно сделать без статических переменных:
use std::str;
use std::io::Read;
use std::net::TcpListener;
pub struct PacketStr<'a>
{
pub src_packet: &'a str, //&str на пакет
pub cs: &'a str,
}
impl<'a> PacketStr<'a>
{
pub fn new(buf:&'a[u8], len:usize) -> Option<PacketStr<'a>>
{
//parse buf and get strings offsets
let src_offset = 1..10;
let cs_offset = 22..33;
Some(
PacketStr
{
src_packet: str::from_utf8(&buf[src_offset]).ok()?,
cs: str::from_utf8(&buf[cs_offset]).ok()?,
}
)
}
}
fn main()
{
let addr = "127.0.0.1:9999";
let listener = TcpListener::bind(addr).unwrap();
println!("Server listening at {}", addr);
let mut buf = [0u8;512];
let mut len = 0;
for stream in listener.incoming() //stream типа TcpStream
{
let mut stream = stream.unwrap();
loop
{
len = stream.read(&mut buf).expect("cant read from stream");
let pkt_strs = PacketStr::new(&buf, len).expect("cant get strings");
//Работа с данными из пакета...
}
}
}
Как начинающему могу посоветовать:
Разберитесь как работают времена жизни в Rust;
Никогда не используйте unsafe и мутабельные статики;
Если выполните п.1, у вас пропадет желание нарушать п.2.
Иногда без unsafe дейстивельно нельзя обойтись, но такое бывает очень редко.
Ваша первая реализация не работает. Нельзя хранить ссылку в той же структуре, где хранятся данные на которые вы ссылаетесь (подробнее см. ссылки в ответе @kpp).
Время жизни в объявлении структуры pub struct Packet<'a>{... - это что-то вроде обобщенного параметра, оно определяется компилятором при создании конкретного экземпляра.
У вас структура владеет массивом, так что время жизни массива равно времени жизни всей структуры Packet. Оно будет разным для каждого экземпляра структуры.