How to get State in FromRequest implementation with Rocket? - rust

I initialized a Mutex<Pool<Postgres>> instance and passed it to manage method on Rocket instance in order to access the database on my controllers. What I usually do is:
// this is purely for example
#[get("/foo/bar")]
pub async fn controller<'a>(
// ... others ...
db_pool: &State<Mutex<PgPool>>,
// ... others ...
) {
// ... do other things ...
let query_r = sqlx::query("SELECT * FROM users WHERE id=$1")
.bind(&id)
.fetch_optional(&mut db_pool.inner().lock().await.acquire().await.unwrap()) // i reach `inner` of state, then `lock` the mutex, lastly `acquire` a pool connection
.await; // type is Result<Option<PgRow>> i suppose, rust-analyzer is not very good at inferring it
// ... do other things ...
}
This is cool and all but here's my problem: I wrote a struct as below...
pub struct User {
id: usize,
username: String,
email: String,
}
...and I actually want to use it as request guard so that the client cannot hit the controllers if they haven't provided the correct credentials.
Official guide of Rocket tells to implement FromRequest trait so that it can be resolved as guard.
However, in order to initialize a User instance, I need to get a PoolConnection inside this FromRequest trait. I have dived into Rocket's API documentation and seems like request parameter on FromRequest has a method named rocket with returns a Rocket<Orbit> instance, and, with this, I can access the state.
However, the problem is that state method returns &Mutex<Pool<Postgres>>, which is an immutable reference, so I cannot do any operations on database with this.
I have come this far and would like to ask if anyone knows a way to access Mutex<Pool<Postgres>> without a reference.
Thanks in advance.
How Far I Have Come
Below is how I have implemented FromRequest.
pub struct User {
id: usize,
username: String,
email: String,
}
#[derive(Debug)]
pub enum UserError {
TokenError(TokenError),
MissingToken,
DatabasePoolError,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for User {
type Error = UserError;
async fn from_request(request: &'r rocket::Request<'_>) -> request::Outcome<Self, Self::Error> {
let encrypted_token_o = request.headers().get_one("Authorization");
match encrypted_token_o {
Some(encrypted_token) => {
let claims_r = Claims::try_from(encrypted_token.to_owned());
match claims_r {
Ok(claims) => {
let db_pool_o = request.rocket().state::<Mutex<PgPool>>();
match db_pool_o {
Some(db_pool) => {
let id = 0; // a test id
let query_r = sqlx::query(
"SELECT id, username, email FROM users WHERE id=$1",
)
.bind(&id)
.fetch_optional(
// ERROR: no method named `inner` found for reference `&rocket::futures::lock::Mutex<Pool<Postgres>>` in the current scope
&mut db_pool.inner().lock().await.acquire().await.unwrap(),
)
.await;
todo!()
}
None => request::Outcome::Failure((
http::Status::InternalServerError,
UserError::DatabasePoolError,
)),
}
}
Err(e) => request::Outcome::Failure((
http::Status::InternalServerError,
UserError::TokenError(e),
)),
}
}
None => request::Outcome::Failure((http::Status::BadRequest, UserError::MissingToken)),
}
}
}
Environment
rustc 1.55.0
rocket 0.5.0-rc.1 with features default and json
sqlx 0.5.7 with features runtime-tokio-native-tls, all-types, postgres and migrate

Related

Reference got from mutex'a value doesn't live enought

I'm learning Rust and trying to create a Admin panel like with egui and multi-threading;
One thread which update the Student struct in an Hashmap && another which run egui and display Student information;
here the problem, I have:
students: Arc<Hashmap<PathBuf, Mutex<Student>>>
actual_student : Option<Mutex<Student>>
// The student in the popup
// Here when I would like to get the &Student after clicking on a button 'See more'
self.actual_student = Some(self.students.get(path.deref()).unwrap());
// But got the error: lifetime may not live enough assignment requires that `'1` must outlive `'a`
Here the complete code of the implementation:
pub struct Maestro<'a> {
pub students: Arc<HashMap<PathBuf, Mutex<Student>>>,
pub logger: Logger,
pub actual_student: Option<&'a Mutex<Student>>,
// Would like to make like this: pub student: Option<&Student> but cannot get reference to
// Student from mutex's value
}
impl<'a> Maestro<'a> {
pub fn new() -> Self {
let config = load_config();
let watcher = config.watcher;
let students = Arc::new(watcher.init());
let mut students_for_thread = Arc::clone(&students);
std::thread::spawn(move || {
watcher.run(&mut students_for_thread);
});
Maestro {
students,
logger: config.logger,
actual_student: None,
}
}
}
impl<'a> eframe::App for Maestro<'a> {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
for (path, student) in self.students.iter() {
if let Ok(mutex) = student.try_lock() {
ui.horizontal(|ui| {
ui.label(&mutex.name);
match &mutex.bashrc_editable {
true => ui.colored_label(egui::Color32::GREEN, "true"),
false => ui.colored_label(egui::Color32::RED, "false"),
};
if ui.button("See more").clicked() {
if self.students.contains_key(path) {
self.actual_student = Some(self.students.get(path.deref()).unwrap());
// FIXME 10/27/22 ectaclick: lifetime may not live long enough assignment requires that `'1` must outlive `'a`
}
}
});
}
}
});
// Would like to create a 'popup' on the right of the list
if self.actual_student.is_some() {
let student = self.actual_student.unwrap().lock().unwrap();
egui::SidePanel::right("student").show(ctx, |ui| {
ui.label(student.name.as_str());
match student.bashrc_editable {
true => ui.colored_label(Color32::GREEN, "true"),
false => ui.colored_label(Color32::RED, "false"),
}
});
}
std::thread::sleep(Duration::from_millis(100));
}
I tried to many type of actual_student:
Option<Student>
Box<Student>
Option<&Student>
Tried to clone the value but the actual_student is not the same of the Student in the Hashmap which is the Student struct updated by the second thread.
But the problem is still the same.
You're getting the lifetime error because if the value is removed the hashmap, the the reference you have in actual_student becomes invalid.
A direct solution to this is to use
Arc<Mutex<Student>> instead of Mutex<Student>
And the use simply use clone of the Arc object, this is relatively lightweight as it is not copying the whole object.
But in general, you're trying to think in OOP. a better approach is to use an id, for example:
actual_student_id : Option<u64>
which means you'll have to use
students.iter().find(|it|it.id == actual_student_id)
everytime you want to get a reference to actual student, but that avoids using Arc, and will probably get rid of Mutex too which is even better as mutexes are generally a source of performance hit, sometimes hard to debug.

Force use of a constructor [duplicate]

This question already has answers here:
How to restrict the construction of struct?
(2 answers)
Closed 5 months ago.
I want to give some business-rule guarantees about certain structs. For example, that an EmailAddress is a valid email, or that a DateRange has a from that lies before a from, and so on. So that when passing such a value around, it is guaranteed to adhere to all business rules for that struct.
struct InvalidEmailAddress;
struct EmailAddress {
value: String
}
impl EmailAddress {
fn new(value: String) -> Result<Self, InvalidEmailAddress> {
if value.contains("#") { // I know, this isn't any sort of validation. It's an example.
Ok(Self { value })
} else {
Err(InvalidEmailAddress)
}
}
}
Ignoring that now new() behaves unexpected (it probably would be better to use a build() method), this brings an issue: When someone builds an EmailAddress through the constructor, it is guaranteed to be "valid". But when someone constructs it as normal struct, it may not be.:
let guaranteed_valid = EmailAddress::new(String::from("hi#example.com")).unwrap();
let will_crash = EmailAddress::new(String::from("localhost")).unwrap()
let unknown_valid = EmailAddress { value: String::from("hi-at-example.com") }
I would like to prohibit any users of those structs from constructing them directly like in the last line.
Is that possible at all? Are there any more ways someone could construct an EmailAddress in an invalid way?
I'm OK with placing the structs in a module, and using public/private visibility if that is possible at all. But from what I can see, any code that wants to now enforce the EmailAddress type, say a send_report(to: EmailAddress) would have access to the struct and can build it directly. Or am I missing something crucial?
You need to place your struct in a module. That way any code outside of that module will only be able to access the public functionality. Since value is not public, direct construction will not be allowed:
mod email {
#[derive(Debug)]
pub struct InvalidEmailAddress;
pub struct EmailAddress {
value: String,
}
impl EmailAddress {
pub fn new(value: String) -> Result<Self, InvalidEmailAddress> {
if value.contains("#") {
// I know, this isn't any sort of validation. It's an example.
Ok(Self { value })
} else {
Err(InvalidEmailAddress)
}
}
}
}
use email::EmailAddress;
fn main() {
let e = EmailAddress::new("foo#bar".to_string()).unwrap(); // no error
//let e = EmailAddress { value: "invalid".to_string() }; // "field `value` of struct `EmailAddress` is private"
}
Playground
More details on visibility in the Book.

How to create a derived column for my struct?

#[derive(Serialize, Deserialize, Debug)]
struct Product {
id: usize,
name: String,
timestamp: i128
}
I deserialize this struct value from a JSON value.
Now I want to expose another property on my struct:
dt: OffsetDateTime
I want this property to be immutable, and set only once. So I don't want to expose a function that like below b/c it would re-calculate each time I call it:
impl Product {
fn dt(&self) -> OffsetDateTime {
OffsetDateTime::from_unix_timestamp_nanos(self.timestamp)
}
}
In java world or other languages I would do something like this:
private dt: OffsetDateTime = null;
public OffsetDateTime getDt() {
if(dt == null) {
dt = OffsetDateTime::from_unix_timestamp_nanos(self.timestamp)
}
return dt;
}
Does Rust have a similar pattern I can use?
You have three options:
Initialize it when initializing the struct, by providing a constructor. This is by far the easiest solution, if initialization isn't expensive or access is common enough that initializing always is not a problem. This is not equivalent to your Java code, however.
Store an Option<OffsetDateTime> and use Option::get_or_insert_with() to initialize it on access. This is cheapier than the third option, but requires a &mut access:
pub fn dt(&mut self) -> &OffsetDateTime {
self.dt.get_or_insert_with(|| { /* Initialization logic */ })
}
Use a library such as once_cell (or the unstable versions in std) to initialize under & access. You can use either Sync or not, depending on whether you need multiple threads to access the data):
pub fn dt(&self) -> &OffsetDateTime {
self.dt.get_or_init(|| { /* Initialization logic */ })
}
You could use an Option to simulate the Java behavior.
struct P {
pub thing: Option<i32>
}
impl P {
pub fn calc_thing( mut self ) -> i32 {
if let None = self.thing {
self.thing = Some(5);
}
self.thing.unwrap()
}
}
fn main(){
let p = P{ thing: None };
println!( "{}", p.calc_thing() );
}

Application state access from an Actix web application middleware

I have a simple middleware intended to access the application's global state to perform validation of the authentication token:
use actix_web;
use actix_web::HttpMessage;
pub struct Authenticator;
impl<S> actix_web::middleware::Middleware<S> for Authenticator {
fn start(
&self,
request: &mut actix_web::HttpRequest<S>,
) -> actix_web::Result<actix_web::middleware::Started> {
//let _state = request.state() as &::application::State;
match request.headers().get("Authentication") {
Some(_) => Ok(actix_web::middleware::Started::Done),
None => Err(::view::error(
"No authentication header provided",
actix_web::http::StatusCode::FORBIDDEN,
)),
}
}
}
The commented string shows how I tried to get the state. I have tried many ways actually. What is the best way of doing such stuff?
I thought about adding a reference to needed data (e.g. Arc'd RwLock) to the Authenticator structure and constructing it with the reference when I register my middlewares.
I am still not good with trait stuff, but there must be a clean way of casting the S type to my application-defined State structure:
pub struct State {
pub database: actix::Addr<actix::Syn, ::database::Actor>,
pub cache: ::std::sync::Arc<::cache::Cache>,
pub sessions: ::std::sync::Arc<::session::Storage>,
}
Use your state instead of S:
impl actix_web::middleware::Middleware<::Application::State> for Authenticator {
}
By the way, middleware can have state too.

In memory database design

I am trying to create an in-memory database using HashMap. I have a struct Person:
struct Person {
id: i64,
name: String,
}
impl Person {
pub fn new(id: i64, name: &str) -> Person {
Person {
id: id,
name: name.to_string(),
}
}
pub fn set_name(&mut self, name: &str) {
self.name = name.to_string();
}
}
And I have struct Database:
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
struct Database {
db: Arc<Mutex<HashMap<i64, Person>>>,
}
impl Database {
pub fn new() -> Database {
Database {
db: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn add_person(&mut self, id: i64, person: Person) {
self.db.lock().unwrap().insert(id, person);
}
pub fn get_person(&self, id: i64) -> Option<&mut Person> {
self.db.lock().unwrap().get_mut(&id)
}
}
And code to use this database:
let mut db = Database::new();
db.add_person(1, Person::new(1, "Bob"));
I want to change person's name:
let mut person = db.get_person(1).unwrap();
person.set_name("Bill");
The complete code in the Rust playground.
When compiling, I get a problem with Rust lifetimes:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:39:9
|
39 | self.db.lock().unwrap().get_mut(&id)
| ^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
40 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 38:5...
--> src/main.rs:38:5
|
38 | / pub fn get_person(&self, id: i64) -> Option<&mut Person> {
39 | | self.db.lock().unwrap().get_mut(&id)
40 | | }
| |_____^
How to implement this approach?
The compiler rejects your code because it violates the correctness model enforced by Rust and could cause crashes. For one, if get_person() were allowed to compile, one might call it from two threads and modify the underlying object without the protection of the mutex, causing data races on the String object inside. Worse, one could wreak havoc even in a single-threaded scenario by doing something like:
let mut ref1 = db.get_person(1).unwrap();
let mut ref2 = db.get_person(1).unwrap();
// ERROR - two mutable references to the same object!
let vec: Vec<Person> = vec![];
vec.push(*ref1); // move referenced object to the vector
println!(*ref2); // CRASH - object already moved
To correct the code, you need to adjust your design to satisfy the following constraints:
No reference can be allowed to outlive the referred-to object;
During the lifetime of a mutable reference, no other reference (mutable or immutable) to the object may exist..
The add_person method already complies with both rules because it eats the object you pass it, moving it to the database.
What if we modified get_person() to return an immutable reference?
pub fn get_person(&self, id: i64) -> Option<&Person> {
self.db.lock().unwrap().get(&id)
}
Even this seemingly innocent version still doesn't compile! That is because it violates the first rule. Rust cannot statically prove that the reference will not outlive the database itself, since the database is allocated on the heap and reference-counted, so it can be dropped at any time. But even if it were possible to somehow explicitly declare the lifetime of the reference to one that provably couldn't outlive the database, retaining the reference after unlocking the mutex would allow data races. There is simply no way to implement get_person() and still retain thread safety.
A thread-safe implementation of a read can opt to return a copy of the data. Person can implement the clone() method and get_person() can invoke it like this:
#[derive(Clone)]
struct Person {
id: i64,
name: String
}
// ...
pub fn get_person(&self, id: i64) -> Option<Person> {
self.db.lock().unwrap().get(&id).cloned()
}
This kind of change won't work for the other use case of get_person(), where the method is used for the express purpose of obtaining a mutable reference to change the person in the database. Obtaining a mutable reference to a shared resource violates the second rule and could lead to crashes as shown above. There are several ways to make it safe. One is by providing a proxy in the database for setting each Person field:
pub fn set_person_name(&self, id: i64, new_name: String) -> bool {
match self.db.lock().unwrap().get_mut(&id) {
Some(mut person) => {
person.name = new_name;
true
}
None => false
}
}
As the number of fields on Person grows, this would quickly get tedious. It could also get slow, as a separate mutex lock would have to be acquired for each access.
There is fortunately a better way to implement modification of the entry. Remember that using a mutable reference violates the rules unless Rust can prove that the reference won't "escape" the block where it is being used. This can be ensured by inverting the control - instead of a get_person() that returns the mutable reference, we can introduce a modify_person() that passes the mutable reference to a callable, which can do whatever it likes with it. For example:
pub fn modify_person<F>(&self, id: i64, f: F) where F: FnOnce(Option<&mut Person>) {
f(self.db.lock().unwrap().get_mut(&id))
}
The usage would look like this:
fn main() {
let mut db = Database::new();
db.add_person(1, Person::new(1, "Bob"));
assert!(db.get_person(1).unwrap().name == "Bob");
db.modify_person(1, |person| {
person.unwrap().set_name("Bill");
});
}
Finally, if you're worried about the performance of get_person() cloning Person for the sole reason of inspecting it, it is trivial to create an immutable version of modify_person that serves as a non-copying alternative to get_person():
pub fn read_person<F, R>(&self, id: i64, f: F) -> R
where F: FnOnce(Option<&Person>) -> R {
f(self.db.lock().unwrap().get(&id))
}
Besides taking a shared reference to Person, read_person is also allowing the closure to return a value if it chooses, typically something it picks up from the object it receives. Its usage would be similar to the usage of modify_person, with the added possibility of returning a value:
// if Person had an "age" field, we could obtain it like this:
let person_age = db.read_person(1, |person| person.unwrap().age);
// equivalent to the copying definition of db.get_person():
let person_copy = db.read_person(1, |person| person.cloned());
This post use the pattern cited as "inversion of control" in the well explained answer and just add only sugar for demonstrating another api for an in-memory db.
With a macro rule it is possible to expose a db client api like that:
fn main() {
let db = Database::new();
let person_id = 1234;
// probably not the best design choice to duplicate the person_id,
// for the purpose here is not important
db.add_person(person_id, Person::new(person_id, "Bob"));
db_update!(db #person_id => set_name("Gambadilegno"));
println!("your new name is {}", db.get_person(person_id).unwrap().name);
}
My opinionated macro has the format:
<database_instance> #<object_key> => <method_name>(<args>)
Below the macro implementation and the full demo code:
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
macro_rules! db_update {
($db:ident # $id:expr => $meth:tt($($args:tt)*)) => {
$db.modify_person($id, |person| {
person.unwrap().$meth($($args)*);
});
};
}
#[derive(Clone)]
struct Person {
id: u64,
name: String,
}
impl Person {
pub fn new(id: u64, name: &str) -> Person {
Person {
id: id,
name: name.to_string(),
}
}
fn set_name(&mut self, value: &str) {
self.name = value.to_string();
}
}
struct Database {
db: Arc<Mutex<HashMap<u64, Person>>>, // access from different threads
}
impl Database {
pub fn new() -> Database {
Database {
db: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn add_person(&self, id: u64, person: Person) {
self.db.lock().unwrap().insert(id, person);
}
pub fn modify_person<F>(&self, id: u64, f: F)
where
F: FnOnce(Option<&mut Person>),
{
f(self.db.lock().unwrap().get_mut(&id));
}
pub fn get_person(&self, id: u64) -> Option<Person> {
self.db.lock().unwrap().get(&id).cloned()
}
}

Resources