Rust – Build Your First Web App with Rocket and SeaORM
github.com
github.com
We are replacing all our diesel code as we found it is just not flexible enough for us and it was just painfull to understand the error messages.
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.
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™.
match x {
Ok(v) => ..., // doing something with the value v
Err(e) => ..., // handling the error e
}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.
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.
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.
Rust is definitely not yet as ergonomic as something like Node for writing web servers. Even if Rocket were 100% production-ready, I probably wouldn't recommend it over a GCd language in my own organization unless we needed a) crazy performance or b) crazy type-safety.
But could Rust get there one day?
> 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
I would call Rust's attempted breadth ambitious. I think it's charting new territory and I'm very curious to see how far it will be able to stretch. I don't know the outcome here; nobody does. It's very possible it will never take off for mainstream web dev and will be confined to systems, tooling, etc. But I think there's potential, and I think it's too early to call.
`(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.
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.)
For what it's worth, a very popular Rust framework (also named SQLx lol) is designed around pure SQL tool.
Personally i think both languages have roughly the same split of SQL vs ORM people. However Rust gives devs way more tooling to to invent, to choose runtime cost, etc. So some types of libraries are just easier in a "complex" language imo.
One remaining problem is writing dynamic queries without giving up static verification completely.
For example, optional WHERE, ORDER BY, GROUP BY clauses are very common. Currently the solutions are:
* Give up and write a fully dynamic query. * Generate every possible permutation. Combinatorial explosion quickly makes this impractical. * Write a static query without the optional clauses, then add clauses and execute a dynamic query. The best of both worlds but complicated.
However the optional clauses are almost always independent from each other. It should be possible to statically verify each one with `SELECT NULL FROM <full set of JOINs> WHERE/ORDER BY/GROUP BY <optional clause>`.
EDIT: Now that I think about it, you could have a partially dynamic query that's still statically checked: new WHERE conditions don't change the structure of the query, so as long as they're valid (which can be statically checked) they can be added to the query without changing the type. Same for e.g. ORDER BY or LIMIT. That would make the combinatorial explosion much more manageable, and it would actually represent different code paths (requesting a different set of fields).
* Give up and write a fully dynamic query.
* Generate every possible permutation. Combinatorial explosion quickly makes this impractical.
* Write a static query without the optional clauses, then add clauses and execute a dynamic query. The best of both worlds but complicated.
ent [1] is what I've been happiest with. It uses code generation, and has an extension system so you can generate your own custom code with the models. I've seen a GraphQL extension and a REST extension, but haven't used either enough to comment beyond that they were easy to set up. Took like 20 minutes and now all my models transparently get GraphQL endpoints.
So far I've been happy with it, but we'll see in a couple of years.