5 ms·
I very much disagree that Pin is as hard as everyone makes it out to be. Using the pin! macros, the pin-project crate, and enough as_mut() to get it to compile
by conradludgate 2y ago
I very much disagree that Pin is as hard as everyone makes it out to be. Using the pin! macros, the pin-project crate, and enough as_mut() to get it to compile and it's not hard at all to get a future impl working. It would be good to get this native (which is what boats wants) so it's easier to discover but it's not at all hard by any means
I think a lot of people think pin is confusing but don't actually try to learn it. When I've sat with people and helped them they understand pretty quickly what pin solves and how it works.
I very strongly think move constructors would be even more complex than pin.
- oldpersonintx 2y ago[dead]
- josephg 2y agoI can only speak from my experience, but I really struggled with it. Ok, I understand moving. Pin is ... not moving. But the struct can still move, just ... not when you have a pointer to the struct. Ok, weird, but ok. Pin has a weird, limited set of functions to access the data fields of the struct. Some are unsafe. And then there's Unpin, which sounds like its not-not-move, so, something can move? No. From std: > Implementing the Unpin trait for T expresses the fact that T is pinning-agnostic: it shall not expose nor rely on any pinning guarantees. So, ??. Then there's macros for pin-project, which most projects in the wild use, but some are unsafe. Why? Which of my fields can I safely expose using pin-project? I tried to implement a custom SSE-style streaming protocol over HTTP, to make a rust server implementation of Braid. I spent about a week trying, including pouring over the implementations of server-sent events and websockets in one of the HTTP libraries. Ultimately I failed to get it to work. (This is before TAIT and some other recent features, so things are probably be better now.) I picked up javascript to write my server instead, and I had the protocol implemented about 20 minutes, in just 20 or so lines of code. I adore rust, and I'd much rather a rust implementation than one based on nodejs. But I ran into skill issues here. Pin and futures in rust are hard. Or at least, I found them hard. I'm sure if I took another crack at it I'd be able to figure it out. But I don't want to spend so many of my brain cells on the language. I want to spend my attention thinking about my problem domain. Like I can in javascript. Rust is an amazing language. But yeah, I really think that pin doesn't meet the standard that the rest of the language sets. I think it could use a rethink.
- withoutboats3 2y agoI'm curious why you needed to deal with `Pin` instead of using async functions. What led you to a path in which you needed to implement poll methods yourself? For what it's worth, all of the practical problems you encountered with using Pin are exactly what my next post is to show how to solve.
- josephg 2y agoI look forward to your next post on the topic then! > I'm curious why you needed to deal with `Pin` instead of using async functions. The protocol I was trying to implement streams messages over time over a single HTTP request thats kept alive for a long time. This is how Server-Sent Events (SSE) works, and its how Google Chat in gmail was first implemented in a way that supported IE 5.5 (!!!). This was a couple years ago now, so the details are a bit fuzzy. And I was relatively new to rust at the time. I was, at the time, still sometimes surprised by the borrow checker. My goal was to make a writable async stream that I could push messages into from other parts of my program. And it also needed backpressure. When you sent messages into the stream, the protocol implementation it encoded them and streamed them into the body of my HTTP response object. I was (I think) using hyper. This is before TAIT was in rust, and for one reason or another I needed to store / reference the future object I was making. (If you use an async fn(), you don't get a name for the Future type the function returns. So I couldn't put the return type in my struct, because I couldn't name it.) So I ended up writing a custom struct that implemented Future, so I could reference the future elsewhere in my code. Hence, implementing Poll myself. I can't honestly remember how Pin came into it all. I think hyper's API for doing this sort of thing stored a Pin<T> or something. I remember at some point trying to write a where clause using higher ranked type bounds to describe the lifetime of a future object that was- or wasn't- associated with the lifetime of the corresponding HTTP request. And that may or may not have been Pinned, and I gave up. It might be fun to revisit this at some point now rust's async support has matured a little. And now that I've matured a lot in how I understand rust. I certainly don't imagine that everyone using async will run into the sort of quagmire that I hit. But this was the first thing I ever really wanted to do with async rust, and it felt horrible to fall on my face trying.