I'm storing data from an external service in a local cache and I want to create an endpoint to return data currently in the cache.
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
use rocket::{Route, State};
use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct Post {
pub title: String,
pub body: String,
}
impl Post {
pub fn new(title: String, body: String) -> Post {
Post { title, body }
}
}
pub struct Cache {
pub posts: Vec<Post>,
}
impl Cache {
pub fn new() -> Cache {
let mut posts = vec![
Post::new(String::from("Article1"), String::from("Blah")),
Post::new(String::from("Article2"), String::from("Blah")),
Post::new(String::from("Article3"), String::from("Blah")),
Post::new(String::from("Article4"), String::from("Blah")),
Post::new(String::from("Article5"), String::from("Blah")),
];
Cache { posts }
}
}
#[derive(Responder)]
pub enum IndexResponder {
#[response(status = 200)]
Found(Json<Vec<Post>>),
#[response(status = 404)]
NotFound(String),
}
#[get("/")]
pub fn index(cache: State<Cache>) -> IndexResponder {
return IndexResponder::Found(Json(cache.posts));
}
fn main() {
rocket::ignite()
.mount("/", routes![index])
.manage(Cache::new())
.launch();
}
The compiler complains:
error[E0507]: cannot move out of dereference of `rocket::State<'_, Cache>`
--> src/main.rs:50:39
|
50 | return IndexResponder::Found(Json(cache.posts));
| ^^^^^^^^^^^ move occurs because value has type `std::vec::Vec<Post>`, which does not implement the `Copy` trait
(cargo build --verbose
output)
My Cargo.toml file:
[package]
name = "debug-project"
version = "0.1.0"
authors = ["Varkal <[email protected]>"]
edition = "2018"
[dependencies]
rocket = "0.4.0"
rocket_contrib = "0.4.0"
serde = { version = "1.0.90", features = ["derive"] }
serde_json = "1.0.39"
Your endpoint does not own the state and thus it cannot return an owned Vec<Post>
. Conceptually, this makes sense because if you did take ownership of it, then what value would be present the next time the endpoint is called?
The simplest thing you can do is clone the data:
#[get("/")]
pub fn index(cache: State<Cache>) -> IndexResponder {
IndexResponder::Found(Json(cache.posts.clone()))
}
This will require that you implement Clone
for Post
, or perhaps change your state to hold something like an Arc
.
A slightly more performant solution is to return a slice that refers to the state. This requires no cloning, but does require using the State::inner
method:
#[derive(Responder)]
pub enum IndexResponder<'a> {
#[response(status = 200)]
Found(Json<&'a [Post]>),
#[response(status = 404)]
NotFound(String),
}
#[get("/")]
pub fn index<'a>(cache: State<'a, Cache>) -> IndexResponder<'a> {
IndexResponder::Found(Json(&cache.inner().posts))
}
See also: