This is one of the nicer ones.
It looks pretty conservative in it's use of Rust's advanced features. The code looks pretty easy to read and follow. There's actually a decent amount of comments (for rust code).
Not bad!
This is one of the nicer ones.
It looks pretty conservative in it's use of Rust's advanced features. The code looks pretty easy to read and follow. There's actually a decent amount of comments (for rust code).
Not bad!
But then there's this Arc, Ref, Pinning and what not - how deep is that rabbit hole?
The _static_ borrow checker can only check what is _statically_ verifiable, which is but a subset of valid programs. There are few things more frustrating than doing something you know is correct, but that you cannot express in your language.
Arc is nothing more than reference counting. C++ can do that too, and I'm sure there are C libraries for it. That's not an admission of anything, it's actually solving the problem rather than ignoring it and hoping it doesn't crash your program in fun and unexpected ways.
Using Arc also comes with a performance hit because validation needs to be done at runtime. You can go back to the faster C/C++ style data exchange by wrapping your code in unsafe {} blocks, though, but the risks of memory corruption, concurrent access, and using deallocated memory are on you if you do it, and those are generally the whole reason people pick Rust over C++ in the first place.
Several kernels for example use type-stable memory, memory that is guaranteed to only hold objects of a particular type, though perhaps only providing that guarantee for as long as you hold an RCU read-lock (this is the case in Linux with SLAB_TYPESAFE_BY_RCU). It is possible in some cases to be able to safely deal with references to objects where the "lifetime" of the referent has ended, but where by dint of it being guaranteed to be the same type of object, you can still do what you want to do.
This comes in handy when you have a problem that commonly appears in kernels where you need to invert a typical lock ordering (a classic case is that the page fault codepath might want to lock, say, VM object then page queue, but the page-replacement codepath will want to lock page-queue then VM object.)
Unfortunately it's hard to think of how the preconditions for these tricks could be formally expressed.
- Technical problem (like connection problems) means I don't know what's in the db
- No technical problem, but no user entry
- No technical problem, and a user entry
You need the Result for the technical problems, and the Option for whether there's a user entry or not.
No. I've written code that returns Result<Option<T>>. It was a wrapper for a server's query web API.
The Result part determines whether the request succeeded and the response is valid.
The Option part is because the parameter being queried might not exist. For example, if I ask the API for the current state of the user session with a given Session ID, but that Session ID does not exist, then the Rust wrapper could return OK(None) meaning that the request succeeded, but that no such session was found.
That is why a query that successfully returns no items can be represented as Ok(None).
A successful query with items returned would instead be Ok(Vec<Item>).
An error in the completing the query (for example, problem with the database), would be Err(DatabaseError) or Err(SomeOtherError).
But in this case, a query using an invalid session ID is not an error. It is asking for details about something that does not exist.
>> cf File::open that could return Result<Option<File>> if file is not found.
This type of query is not like File::open which gets a handle to a resource. Trying to get a handle to a resource that does not exist is an error.
This type of query is read-only and does not allocate any resources or prepare to do anything with the session.
It simplifies the control flow because it distinguishes between errors in completing a query versus the presence or absence of items returned from the query.
But in that case you're stuck paying the overhead 100% of the time, even though 90% of the lifetimes are simple. (Perhaps a little less so with escape analysis etc., but doing it at compile time in a way that's understandable in the source feels a lot more reliable)
Documentation and how SQL database queries work.
The documentation states that a valid session id will return a SessionInfo struct (since it is an Option the type is Some(SessionInfo) ), and that an invalid session id will return None.
If the SQL query is something like "SELECT * FROM USER_SESSIONS WHERE USER_SESSION_ID = $1" then if an invalid session id is provided the database returns zero rows. The query was successful, but there were no matching sessions with that session id.
>> You have conflated empty response with an erroneous situation. The simplest solution is just Result<T, E>.
Again, an empty response is not an error in this situation. If your database query returns zero rows, is it an error? The database query succeeded. There are no sessions with the provided session id. What error occurred?