3 ms·
You kinda can do this in Rust at the moment with async, in the sense of: https://docs.rs/async-stream/latest/async_stream/ https://docs.rs/async-stream/latest/
by tekacs 3y ago
You kinda can do this in Rust at the moment with async, in the sense of:
https://docs.rs/async-stream/latest/async_stream/ https://docs.rs/async-stream/latest/async_stream/
So something like this, off the top of my head. It's not the most Rust-native code because I tried to match your code block as closely as possible to make the parallels more obvious.
struct TCPHandle {
sender: Sender<TCPPacket>,
// I'm taking liberties here, you can't use impl like this
// so you'd have to engage in some shenanigans which I don't
// want to type right now...
stream_to_client: impl Stream<Item = Result<TCPPacket, TCPError>>,
}
fn tcp_session() -> TCPHandle {
let (sender, receiver) = channel();
let stream_to_client = stream! {
let syn_packet = receiver.recv().await;
if (!check_syn_flag(syn_packet)) {
yield Err(TCPError::BadInputError);
return;
}
let syn_ack_packet = build_tcp_segment(...);
yield Ok(syn_ack_packet);
let ack_packet = receiver.recv().await;
if (!check_ack_flag(ack_packet)) {
yield Err(TCPError::BadInputError);
return;
}
// ... proceed with TCP session
}
TCPHandle { sender, stream_to_client }
}
let tcp_connections = HashMap<(Ipv4Addr, u16), TCPHandle>;
loop {
let incoming_ip_packet = read_ip_packet_from_raw_socket().await;
let handle = tcp_connections.get_mut(get_dst(&incoming_ip_packet));
handle.sender.send(incoming_ip_packet).await;
let result = handle.stream_to_client.next().await;
match result {
Ok(packet) => send_ip_packet_to_raw_socket(packet).await,
Err(e) => { ... handle errors },
}
}
I'm curious how something like this would work for your purposes... you could probably wrap the whole thing up a little more and make it behave even more like your example?
- 10000truths 3y agoI'm aware that encapsulated control flow like this exists at a syntactic level, but the implementation deficiencies remain unaddressed: 1. No access to the actual concrete type, as you mentioned, which means you can't do Vec<Stream<...>> - you have to resort to indirections like Vec<Box<dyn Stream<...>>>. You might be able to work around this somehow if you were determined enough (DWARF debug info and std::mem::transmute come to mind), but any such approach would result in an ugly and exceedingly fragile abomination. 2. Brittle, if any, optimization. `stream!` is basically async/await with proc macro syntax dressing, and Rust's async/await implementation still suffers the same sort of bloated frame size issues as generators do, under certain circumstances. To provide some context, suppose I want to write a custom event loop that handles millions of concurrent sessions (could be filesystem transactions, or TCP sessions, or some other I/O). At that level, every kilobyte of per-session data I add equates to additional gigabytes of memory usage. Every byte of space used by the compiler-generated state machine has to be carefully accounted for. I can't afford to have my per-session context blowing up in size because the compiler naively duplicates stack slots for arguments across yield points [1], or because one of my local variable types implements Copy [2], or because the compiler will simply fail to optimize local variables that are never live across a yield point [3]. I suppose if I care so much about memory usage, I could just draw up my own state machine, mentally track the liveness of each state variable and lay out a hand-written size-optimized struct accordingly, but that's going to be a painful exercise and a maintainability nightmare. [1] https://github.com/rust-lang/rust/issues/62958 https://github.com/rust-lang/rust/issues/62958 [2] https://github.com/rust-lang/rust/issues/62952 https://github.com/rust-lang/rust/issues/62952 [3] https://github.com/rust-lang/rust/issues/59087 https://github.com/rust-lang/rust/issues/59087
- nicoburns 3y agoRust also has actual generators, which are used to implement async/await. They're unstable, but they are accessible from user code in nightly Rust https://doc.rust-lang.org/beta/unstable-book/language-features/generators.html https://doc.rust-lang.org/beta/unstable-book/language-featur...