4 ms·
I look at this, and I can't shake the feeling that it's overly complicated. to_inner, to_owner, Box::pin...
by dmitriid 5y ago
I look at this, and I can't shake the feeling that it's overly complicated. to_inner, to_owner, Box::pin...
- johnisgood 5y agoYeah, or: Post::find_by_id(id).one(db).await.unwrap().unwrap().into(); I assume this project uses idiomatic Rust code. Is `unwrap().unwrap()` really necessary? Things like this really make Rust unappealing to me.
- dljsjr 5y agoNo this is not idiomatic Rust code and it's a problem I personally have with a lot of Rust example code. In practice most people would say that you should avoid using unwrap unless there is absolutely no other way, and in this example you could definitely avoid using unwrap. The code would be a wee bit more verbose but it would be more readable (to me, anyway) and it would avoid a potential crash. edit to clarify: In Rust, "nulls" and "errors" are part of the type system as structs, with "Option" for nullable types and "Result" for fallible types. "unwrap" is a function that you use if you "know" that the result is 100% not null or an error. Because if you call unwrap on a result that is null or an error, it causes a hard crash. So the only time you use unwrap is if you know you have a situation where a null or an error is unrecoverable and your program should just give up. This is definitely not one of those cases, which is why unwrap is not idiomatic here. It's the equivalent in Java of an uncaught RuntimeException or an uncaught NPE. And it's setting up newbies for failure when they see this and think that it's The Way™.
- davidkunz 5y agoExactly! Just to add how to deal with Result types in a safer way (as an example): match x { Ok(v) => ..., // doing something with the value v Err(e) => ..., // handling the error e }
- Kinrany 5y ago`.unwrap()` is for ignoring possible errors. Normally you either short-circuit and return the error from the current function with `?` (similar to exceptions but explicit), ignore the error with a more explicit `.expect("The reason this error will never happen")` or handle the error with pattern matching.
- nauticacom 5y agoIt's because Rust's fundamental concerns—ownership, error handling, mutability, etc.—are at a much lower level than most applications need to deal with. Rust has the (imo unrealistic) goal of trying to fit an incredibly broad range of use-cases, from embedded microcontrollers to command-line tools to web frameworks, each of which has a different level of abstraction and concerns that are important. When you go up the ladder of abstraction like in this project, stack vs heap, ownership, et al get in the way of dealing with application logic.
- brundolf 5y agoThe thing though is you can build higher-level abstractions on top of the base language (and Rust’s type system helps keep these abstractions from getting shaky). Macros also help a lot with eliminating ceremony, as demonstrated really well by Rocket. I agree it’s stretching for a wide swath of cases, and it will be interesting to see how much of that space it ends up becoming popular for, but I’m not sure I’d call it “unrealistic”. Part of the problem right now - especially in the web space - isn’t the language but a lack of time investment in the ecosystem. Rocket is the most promising web framework Rust has, and its current release has been in RC for 6 months (!). I really hope it gets corporate backing at some point.
- nauticacom 5y ago> The thing though is you can build higher-level abstractions on top of the base language That's true, but since the base language is so low-level there are certain things you can't get away from. You can never get away from the fact that String/&str and Vec/&[] are different. If I'm writing code where every allocation counts, then I need a language to be able to, for example, give me a substring without allocating. If I'm writing web code, though, the number of allocations is probably in the thousands or tens of thousands and I just don't care. But using Rust, I have to care about the difference between allocated types and slices, which is unnecessary complexity. You can never get away from the fact that heap allocations are explicit, so most kinds of dynamism, since they rely on runtime polymorphism, need to go through Boxes or generic types or something else. I can't count the number of time's I've needed to Box something because I needed runtime polymorphism, or needed to rework an interface because I had Boxed something but now it's not object-safe. You can never get away from Rust's ownership model, which wreaks havoc on graph-like structures that are common in a domain model (which you'll undoubtedly have in your web app/api. It's no big deal for a language with a GC, but because the memory semantics are difficult, Rust forces that difficulty onto you. Again, it would make sense if I only had some tiny amount of RAM to work with on a microcontroller, but I can spare gigs of server RAM. All of these (and others, these are just examples) add up to complexity that is unnecessary and overwhelming for the level of abstraction that web projects deal with. Rust certainly has many great things, but it's a trade off, and imo it's one that's not worth it. Personally, I think tools are better when they target a specific audience with specific goals and specific use-cases. I suppose I stretched calling it "unrealistic" for Rust, since, well, look where it is; I guess it's more just a position I don't understand or agree with. Honestly the best part of Rust is (most of) the community. Most of the people I interact with are incredibly friendly, welcoming, and interested in making the community better. I wish Swift had half the community Rust does, and wasn't so Apple-controlled, because it feels like an applications-friendly version of Rust.
- ofrzeta 5y agoThat Rust syntax (I see on the SQLx page) is really appalling. What's "Ok(())" supposed to mean and why are there two pairs of braces. There almost nothing immediately comprehensible and what's comprehensible seems overly complicated: "let row: (i64,)" - what's with that trailing comma here?
- CodesInChaos 5y agoA `Result<T, Error>` can either be `Ok(value)` or `Error(value)` where value must be of type `T` or `Error` respectively. If you don't want to carry any information you use the unit type `()`, which has a single value `()`. So in `Ok(())` the outer parentheses are for constructing the OK result, while the inner parentheses are the unit value. `(i64,)` is a tuple with a single component, the trailing comma disambiguates it from plain parentheses. I haven't needed one of those so far.
- gwbas1c 5y agoLast year between jobs I spent some time with Node.js and Rust. Rust, as a language, isn't a good choice for simple web applications. It's compiler-checked manual memory management. Most of the time I spent with Rust was just getting boggled down in arcane syntax that would probably take me months to become familiar with. In contrast, Node.js with garbage collection is blazingly fast for web applications. Rust, though, introduces some really good concepts that I'd like to see in other languages. Matching and error handling takes some time to get used to, but it's much more advanced than exceptions. (Specifically, the caller, not the callee, decides which errors are exceptional and which errors it can pass up the stack.)