RUST find value in web::Data<Arc<>> - rust

my "main" file
mod router;
mod student;
use std::sync::Arc;
use crate::router::init_router;
use crate::router::Memory;
use actix_web::{App, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
dotenv::dotenv().ok();
let repo = Arc::new(Memory::repository());
let host = std::env::var("SERVER.HOST").unwrap_or("127.0.0.1".to_string());
let port = std::env::var("SERVER.PORT").unwrap_or("8080".to_string());
let url = format!("{}:{}", host, port);
println!("url: {}", &url);
HttpServer::new(move || {
App::new()
.data(repo.clone()) // shared
.configure(init_router)
})
.bind(&url)?
.run()
.await
}
my file: "router.rs"
use std::sync::Arc;
use crate::student::Student;
use actix_web::{get, web, HttpResponse, Responder};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct Memory {
pub students: Vec<Student>,
}
impl Memory {
fn new() -> Self {
Memory {
students: Vec::new(),
}
}
pub fn repository() -> Self{
Self {
students: vec![
{Student::new("1".to_string(), "Daniel".to_string(), 19)},
{Student::new("2".to_string(), "Lucia".to_string(), 17)},
{Student::new("3".to_string(), "Juan".to_string(), 14)}
]
}
}
}
#[get("/student/list/all")]
async fn list_all(repo: web::Data<Arc<Memory>>) -> impl Responder {
HttpResponse::Ok().json(&***repo)
}
#[get("/student/list/by-id/{id_student}")]
async fn list_by_id(web::Path(id_student): web::Path<String>, repo: web::Data<Arc<Memory>>) -> impl Responder {
HttpResponse::Ok().json(&***repo.students.into_iter().find(|x| *x.id == id_student))
}
pub fn init_router(config: &mut web::ServiceConfig) {
config.service(list_all);
config.service(list_by_id);
}
and my file: "student.rs"
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct Student{
pub id: String,
pub nombre: String,
pub calificacion: u32,
}
impl Student{
pub fn new(id: String, nombre: String, calificacion: u32) -> Self{
Self{id, nombre, calificacion}
}
}
I want to show a student in the following path: 127.0.0.1:3000/student/list/by-id/1
but i have the following error
error[E0614]: type `std::vec::IntoIter<Student>` cannot be dereferenced
--> src\router.rs:43:33
|
43 | HttpResponse::Ok().json((&***repo.lock().unwrap().students.into_iter()).find(|x| *x.id == id_student))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
because I get the error, I don't know what is wrong. Please I need help I am new to this programming language.

The dot operating will smartly dereference the pointer, so the following will compile:
&repo.students.iter().find(|x| *x.id == id_student)
the Arc will be dereferenced when accessing students from repo, which will give a reference to the Vec, and .iter() will give you a non-consuming iterator, which you can then map over (.into_iter() will require the Vec to be copied and consumed)

Related

Share memory data by REST parameters in Rust

my "main" file
mod router;
mod student;
use std::sync::Arc;
use crate::router::init_router;
use crate::router::Memory;
use actix_web::{App, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
dotenv::dotenv().ok();
let repo = Arc::new(Memory::repository());
let host = std::env::var("SERVER.HOST").unwrap_or("127.0.0.1".to_string());
let port = std::env::var("SERVER.PORT").unwrap_or("8080".to_string());
let url = format!("{}:{}", host, port);
println!("url: {}", &url);
HttpServer::new(move || {
App::new()
.data(repo.clone()) // shared
.configure(init_router)
})
.bind(&url)?
.run()
.await
}
my file: "router.rs"
use std::sync::Arc;
use crate::student::Student;
use actix_web::{get, web, HttpResponse, Responder};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct Memory {
pub students: Vec<Student>,
}
impl Memory {
fn new() -> Self {
Memory {
students: Vec::new(),
}
}
pub fn repository() -> Self{
Self {
students: vec![
{Student::new("1".to_string(), "Daniel".to_string(), 19)},
{Student::new("2".to_string(), "Lucia".to_string(), 17)},
{Student::new("3".to_string(), "Juan".to_string(), 14)}
]
}
}
}
#[get("/studen/list/all")]
async fn list_all(repo: web::Data<Arc<Memory>>) -> impl Responder {
HttpResponse::Ok().json(repo)
}
pub fn init_router(config: &mut web::ServiceConfig) {
config.service(list_all);
}
and my file: "student.rs"
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct Student{
pub id: String,
pub nombre: String,
pub calificacion: u32,
}
impl Student{
pub fn new(id: String, nombre: String, calificacion: u32) -> Self{
Self{id, nombre, calificacion}
}
}
so I want to show all students in a json via the following path: 127.0.0.1:3000/student/list/all
but i have the following error
| HttpResponse::Ok().json(repo)
| ^^^^ the trait Serialize is not implemented for Data<Arc<Memory>>
I still can't pass this data by parameter of a GET method, I need a little help please to display it in json. It is necessary because later I will need this data by parameter to add or remove.
(Note that you typed #[get("/studen/list/all")] instead of #[get("/student/list/all")] in your original code.)
You don't want to serialize the Data<Arc<Memory>> to JSON, you only want to serialize the Memory. To do so, you can dereference the Data<Arc<Memory>> to get an Arc<Arc<Memory>>, then dereference the Arcs twice to get a Memory. Then, you add a reference as to not require cloning the resulting Memory. So, you replace
HttpResponse::Ok().json(repo)
with
HttpResponse::Ok().json(&***repo)
However, Data is already a wrapper around an Arc, so no Arc<Memory> in the is required. If you want to edit the Memory eventually, you'll have to add a Mutex inside. So, you'll have to obtain a lock on repo before serializing it.

How to properly solve lifetime inferences in struct?

The Rust playground code is here.
I have a struct of Token which has lifetime 'tok, and a scanner has lifetime 'lexer. I'm using both of them in another struct Parser, then I had a problem:
pub struct Token<'tok> {
pub value: Cow<'tok, str>,
pub line: usize,
}
pub struct Scanner {
pub source: Vec<char>,
pub current: usize,
pub line: usize,
}
pub struct Parser<'lexer> {
pub curr: &'lexer Token<'lexer>,
pub prev: &'lexer Token<'lexer>,
scanner: &'lexer mut Scanner,
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&mut self) {
self.prev = self.curr;
self.curr = &self.scanner.next(); // cannot inference lifetime
}
}
I think the problem is Token has lifetime 'tok, and the borrow checker doesn't know the relation between 'tok and 'lexer so it can't inference proper lifetime.
However, I can avoid the problem by modifying it into the updated code:
pub struct Parser<'lexer> {
pub curr: Token<'lexer>,
pub prev: Token<'lexer>,
scanner: &'lexer mut Scanner,
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&mut self) {
let prev = std::mem::replace(&mut self.curr, self.scanner.next());
self.prev = prev;
}
}
And with Token produced by next() is static:
impl Scanner {
pub fn next(&mut self) -> Token<'static> {
Token {
value: Cow::from(""),
line: 0,
}
}
}
It does compile but I think it's not ideal because all tokens are cloned from scanner into parser(they are not references anymore) and living until end of Parser's life. So memory usage is doubled. Is there a more proper way to deal with this?
Actualy your code structure is not ideal to deal with the borrow checker here why:
Token struct should be owned, the struct itself do not require any allocations (as the token is owned some indrection are required to allow prev <-> next switching)
None of the Parser or Lexer should own the base data so it will be easy to bound proper lifetime
In our case Vec<Char> is not a friendly type to work with (we do not need to own the data and this will be harder to make the comipler understand lifetimes), instead we're going to use an &'str but you could reproduce the exact behaviours with an &[char])
Here is an example that compile just fine
pub struct Token<'source> {
pub value: Cow<'source, str>,
pub line: usize,
}
pub struct Scanner<'source> {
pub source: &'source str,
pub current: usize,
pub line: usize,
}
pub struct Parser<'source> {
pub curr: Option<Token<'source>>,
pub prev: Option<Token<'source>>,
scanner: Scanner<'source>,
}
impl <'source>Scanner<'source> {
pub fn next(&'source /* DONT Forget to bound 'source to `self` */ self) -> Token<'source> {
Token {
value: Cow::from(self.source), /* `self.source` is bound to `'source` so the compiler understand that the token lifetime is the same than the source's one */
line: 0,
}
}
}
impl <'lexer> Parser<'lexer> {
pub fn advance(&'lexer mut self) {
self.prev = self.curr.take();
self.curr = Some(self.scanner.next());
}
}

How do you get the json from an use actix_web::HttpRequest into struct?

What is the simplest way to get json from the HttpRequest into a struct you created. Here is the main.rs
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.data(web::JsonConfig::default().limit(4096))
.data(connect())
.service(web::resource("/insert").route(web::post().to(handlers::tours::insert)))
})
.bind("127.0.0.1:8088")
.unwrap()
.run()
.await
}
And here is the struct in model/tour.rs:
#[derive(Serialize, Deserialize, Insertable)]
pub struct TourForm {
pub name: String,
}
And here is the handler in handlers/tours.rs::
pub async fn insert(
tour_form: web::Json<TourForm>,
pool: web::Data<MysqlPool>,
) -> Result<HttpResponse, HttpResponse> {
Ok(HttpResponse::Ok().json(&tour_form.name))
}
I tried this variation because I thought this would make the code very simple:
pub async fn insert(
tour_form: TourForm,
pool: web::Data<MysqlPool>,
) -> Result<HttpResponse, HttpResponse> {
Ok(HttpResponse::Ok().json(&tour_form.name))
}
But got the error:
^^ the trait `actix_web::extract::FromRequest` is not implemented for `model::tour::TourForm`
Should I implement the FromRequest function into the TourForm struct or is there an easier way?
I was able to get the TourForm object out of the web::Json simply by doing tour_form.0
pub async fn insert(
tour_form: web::Json<TourForm>,
pool: web::Data<MysqlPool>,
) -> Result<HttpResponse, HttpResponse> {
Ok(HttpResponse::Ok().json(&tour_form.0))
}

Creation of a hashmap with struct in Rust

I am trying to set up a hashmap of objects / structs in rust... But I don't understand this concrete problem (a lifetime error).
#[derive(Hash, Eq, PartialEq)]
#[derive(Serialize, Deserialize, Debug)]
pub struct Node<'a> {
identifier: &'a str,
sha_id: Vec<u8>,
successor_id: Option<Vec<u8>>,
predecessor_id: Option<Vec<u8>>,
}
impl<'a> Node<'a> {
...
..
.
}
pub struct Application<'a> {
hash_map: HashMap<&'a str, Node>,
}
impl<'a> Application<'a> {
fn join(&self, node: &Node) {
self.hash_map.insert(node.identifier, node);
}
}
The error is a missing lifetime specifier in the hash_map: HashMap<&'a str, Node> that I tried to solve changing Node to Node<'a> but It throws a "mismatched type" error when I try to insert...
I don't exactly why I have this problem missing the lifetime and I don't find solutions..
UPDATE:
#[derive(Hash, Eq, PartialEq)]
#[derive(Serialize, Deserialize, Debug)]
pub struct Node<'a> {
identifier: &'a str,
sha_id: Vec<u8>,
successor_id: Option<Vec<u8>>,
predecessor_id: Option<Vec<u8>>,
}
impl<'a> Node<'a> {
...
..
.
}
pub struct Application<'a> {
hash_map: HashMap<&'a str, Node<'a>>,
}
impl<'a> Application<'a> {
fn join(&self, node: &Node) {
self.hash_map.insert(node.identifier, *node);
}
}
And the output is:
"explicit lifetime required in the type of `node`"
UPDATE2:
pub struct Application<'a> {
hash_map: HashMap<&'a str, Node<'a>>,
}
impl<'a> Application<'a> {
fn join(&mut self, node: &'a Node<'a>) {
self.hash_map.insert(node.identifier, *node);
}
}
And the output is:
self.hash_map.insert(node.identifier, *node); cannot move out of borrowed content
COMPLETE SOLUTION
#[derive(Clone, Hash, Eq, PartialEq)]
#[derive(Serialize, Deserialize, Debug)]
pub struct Node<'a> {
identifier: &'a str,
sha_id: Vec<u8>,
successor_id: Option<Vec<u8>>,
predecessor_id: Option<Vec<u8>>,
}
impl<'a> Node<'a> {
...
..
.
}
pub struct Application<'a> {
hash_map: HashMap<&'a str, Node<'a>>,
}
impl<'a> Application<'a> {
fn join(&mut self, node: Node<'a>) {
self.hash_map.insert(node.identifier, node);
}
}
This simplified example seems to work:
use std::collections::HashMap;
#[derive(Clone)] // we'll be cloning it later on
struct Node<'a> {
data: &'a i32
}
struct Test<'a> {
hash_map: HashMap<&'a str, Node<'a>> // the hash map owns the struct
}
impl<'a> Test<'a> {
fn new() -> Test<'a> {
Test {hash_map: HashMap::new()}
}
fn join(
&mut self, // must be mutable
node: Node<'a>) { // do not pass a reference
self.hash_map.insert("test", node); // inserting moves `node`
}
}
fn main() {
let stuff = Node {data: &12};
let mut test = Test::new();
test.join(stuff.clone()); // if we don't clone, `stuff` will get moved
println!("{}", *test.hash_map["test"].data); // outputs "12"
}
Since std::collections::HashMap::insert attempts to move its second argument, one can't dereference a pointer to something and pass that to this method because otherwise the pointer will become uninitialized, which isn't permitted. A way so solve this is to pass a moved value and not a pointer to join.
For poor idiots like myself, who are trying to find out how to put hashmaps in a struct, no need to spend many hours "playing" with lifetimes(the 'a in the above example). They are not required in the slightest, just use String instead of &str in your structure.
struct ComputerConfig {
hostname: String,
// displays: Vec<DispConfig>,
}
struct MyConfig {
pub config_version: u8,
computers: HashMap<String, ComputerConfig>, // the hash map owns the struct
}
impl MyConfig {
fn new() -> MyConfig {
MyConfig {
computers: HashMap::new(),
config_version: 1,
}
}
/// Join is used to add a new ComputerConfig into the hashmap
fn join(
&mut self, // must be mutable
key: &str,
node: ComputerConfig,
) {
// do not pass a reference
self.computers.insert(key.to_string(), node); // inserting moves `node`
}
}
fn main() {
let mut cfg = MyConfig::new()
cfg.join("test", stuff);
println!("{:?}", &cfg); // outputs "12"
}

Use trait as interface for database entity

I am trying to make an Entity interface for dynamically mapping a database result into a Rust struct:
pub trait Entity {
fn map(&self, Result<QueryResult>) -> Self;
}
pub struct DbQuery<T> {
pub sql: String,
pub params: Vec<Value>,
pub limit: i32,
pub paged: Option<Pagination>,
pub entity: T,
}
pub struct Settings {
pub name: String,
pub value: Option<String>,
}
impl Entity for Settings {
fn map(&self, result: Result<QueryResult>) -> Settings {
// ...
Settings {
name: "hello".to_string(),
value: None,
}
}
}
impl DbMapper {
// ...
pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>> {
println!("query find SQL: {}", query.sql);
let mut stmt = &self.pool.prepare(query.sql).unwrap();
let ret = Vec::new();
for row in stmt.execute(query.params).unwrap() {
ret.push(query.entity.map(row.unwrap()));
}
Some(ret)
}
}
But I get an error:
error: no method named map found for type T in the current scope
ret.push(query.entity.map(row.unwrap())); |
note: the method map exists but the following trait
bounds were not satisfied: T : std::iter::Iterator = help: items
from traits can only be used if the trait is implemented and in scope;
the following traits define an item map, perhaps you need to
implement one of them: = help: candidate #1:
models::holders::database::Entity = help: candidate #2:
std::iter::Iterator
Here is a version of your code that runs on the playground and replicates your issue:
pub struct QueryResult;
pub struct Value;
pub struct Pagination;
pub struct DbMapper;
pub trait Entity {
fn map(&self, Result<QueryResult, ()>) -> Self;
}
pub struct DbQuery<T> {
pub sql: String,
pub params: Vec<Value>,
pub limit: i32,
pub paged: Option<Pagination>,
pub entity: T,
}
pub struct Settings {
pub name: String,
pub value: Option<String>,
}
impl Entity for Settings {
fn map(&self, result: Result<QueryResult, ()>) -> Settings {
// ...
Settings {
name: "hello".to_string(),
value: None,
}
}
}
impl DbMapper {
// ...
pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>> {
println!("query find SQL: {}", query.sql);
// ########## attempt to call map()
let _ = query.entity.map(Ok(QueryResult {}));
let ret = Vec::new();
Some(ret)
}
}
fn main() {}
The problem is that T in the DbQuery<T> argument in the find method has no idea that T is an Entity type. So we need to tell it:
pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>>
where T: Entity
{
// ... code here ...
}
This now compiles and runs.
The compiler now knows that T is an Entity of some description, and it can call the map method on it.

Resources