I'm working on some code whose structure is similar to that of "middlewares" in web frameworks, so I'll use that terminology in this minimal example. The idea of a middleware function is that it wraps the actual response handler, so it can modify the request before it goes to the handler, and modify the response after it comes back:
struct Request;
struct Response;
trait Handler {
fn handle(&self, request: Request) -> Response;
}
trait Middleware {
fn handle(&self, request: Request, next: Box<dyn FnOnce(Request) -> Response>) -> Response;
}
I considered having two separate functions, one to preprocess the request and one to postprocess the response, but then I wouldn't be able to store any additional state during the request without resorting to hacks.
The server contains dynamically configured handlers and middlewares, so it has to use some boxed trait objects:
struct Server {
handler: Box<dyn Handler>,
middlewares: Vec<Box<dyn Middleware>>,
}
Now, how to implement the response handling? Here are my two attempts:
impl Server {
// First attempt: using Iterator::fold()
fn handle_request_fold<'a>(&'a self, request: Request) -> Response {
let handler_with_middlewares = self.middlewares.iter()
.rev()
.fold::<Box<dyn FnOnce(Request) -> Response + 'a>, _>(
Box::new(|request| self.handler.handle(request)),
|next, middleware| {
Box::new(|request| middleware.handle(request, next))
}
);
handler_with_middlewares(request)
}
// Second attempt: using recursion
fn handle_request_recurse(&self, request: Request) -> Response {
self.handle_request_from(request, 0)
}
fn handle_request_from<'a>(&'a self, request: Request, index: usize) -> Response {
if index >= self.middlewares.len() {
self.handler.handle(request)
} else {
let next = Box::new(
|r: Request| self.handle_request_from(r, index + 1));
self.middlewares[index].handle(request, next)
}
}
}
Both attempts give the same error, which suggests that I'm doing something fundamentally wrong here:
error[E0759]: `self` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
--> src/lib.rs:25:67
|
19 | fn handle_request_fold<'a>(&'a self, request: Request) -> Response {
| -------- this data with lifetime `'a`...
...
25 | Box::new(|request| middleware.handle(request, next))
| ^^^^ ...is captured and required to live as long as `'static` here
I know that trait objects have an implicit 'static lifetime, so I tried adding explicit lifetimes as you can see, but it didn't help. I don't understand why the compiler would demand a 'static lifetime anywhere here; nothing (including the closures) can escape the handle_request_* functions, right?
Playground link
And just after posting, I figured it out: the boxed trait object passed to the Middleware needs a non-'static lifetime as well.
trait Middleware {
fn handle<'a>(&self, request: Request,
next: Box<dyn FnOnce(Request) -> Response + 'a>)
-> Response;
}
fn handle_request_from(&self, request: Request, index: usize) -> Response {
if index >= self.middlewares.len() {
self.handler.handle(request)
} else {
let next = Box::new(
move |request| self.handle_request_from(request, index + 1));
self.middlewares[index].handle(request, next)
}
}
Related
This question is written for Yew v0.19
Asynchronous foreign JavaScript functions can be used in Rust through Closures, as the function to pass-in:
#[wasm_bindgen]
extern "C" {
fn setInterval(closure: &Closure<dyn FnMut()>, time: u32) -> i32;
}
// ...
let cb = Closure::new(|| {
log("interval elapsed!");
});
let interval_id = setInterval(&cb, 1_000);
This is nice for a pedantic examples, but Closures have a critical requirement - the function applied needs to have a 'static lifetime. Likewise, with Yew applications, a perfect mechanism for spontaneous response is the Message enum, and having it update() the Model. However, the link() mechanism in Context (which issues messages) does not have a static lifetime.
In an ideal world, the value submitted to the closure could just be applied as a Yew component message:
struct Model {
thing: Option<JsValue>,
}
enum Msg {
GotThing(JsValue),
}
#[wasm_bindgen]
extern "C" {
fn createThing(closure: &Closure<dyn FnMut(JsValue) -> ());
}
impl Component for Model {
type Message = Msg;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Model {
thing: None, // initial value
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::GotThing(x) => { // handle the message
self.thing = Some(x);
true
},
}
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
if first_render {
let cb: Box<dyn FnMut(JsValue) -> ()> = Box::new(|x| {
// try and issue the message
ctx.link().send_message(Msg::GotThing(x));
// ^ doesn't have a `'static` lifetime! Won't compile
});
createThing(Closure::wrap(&cb));
}
}
// fn view() ... omitted, not relevant
}
I'm wondering if there's a way to convert a Callback into a Closure, or if there's a better, more canonical way to do this, to please correct me.
Another idea I had would use some kind of queue defined statically (which wouldn't be safe as it's a mutable static variable), but then it could be used as an intermediary data type between the Closure passed to createThing, and messages could be dispatched within the component.
Maybe there's an external way to interact with a Yew component that I'm overlooking? I'm not sure how to resolve this issue. What would be the most correct way to achieve this goal?
I have two structs:
Client, which stores a callback and calls it in response to receiving new data. As an example, you can think of this as a websocket client, and we want to provide a hook for incoming messages.
BusinessLogic, which wants to hold a Client initialized with a callback that will update its local value in response to changes that the Client sees.
After following compiler hints, I arrived at the following minimal example:
Rust playground link
use rand::Rng;
struct Client<'cb> {
callback: Box<dyn FnMut(i64) + 'cb>,
}
impl<'cb> Client<'cb> {
fn do_thing(&mut self) {
// does stuff
let value = self._get_new_value();
// does more stuff
(self.callback)(value);
// does even more stuff
}
fn _get_new_value(&self) -> i64 {
let mut rng = rand::thread_rng();
rng.gen()
}
}
struct BusinessLogic<'cb> {
value: Option<i64>,
client: Option<Client<'cb>>,
}
impl<'cb> BusinessLogic<'cb> {
fn new() -> Self {
Self {
value: None,
client: None,
}
}
fn subscribe(&'cb mut self) {
self.client = Some(Client {
callback: Box::new(|value| {
self.value = Some(value);
})
})
}
}
fn main() {
let mut bl = BusinessLogic::new();
bl.subscribe();
println!("Hello, world!");
}
Problem is, I am still getting the following compiler error:
Compiling playground v0.0.1 (/playground)
error[E0597]: `bl` does not live long enough
--> src/main.rs:51:5
|
51 | bl.subscribe();
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
...
54 | }
| -
| |
| `bl` dropped here while still borrowed
| borrow might be used here, when `bl` is dropped and runs the destructor for type `BusinessLogic<'_>`
For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` due to previous error
I understand why I'm seeing this error: the call to subscribe uses a borrow of bl with a lifetime of 'cb, which is not necessarily contained within the scope of main(). However, I don't see how to resolve this issue. Won't I always need to provide a lifetime for the callback stored in Client, which will end up bleeding through my code in the form of 'cb lifetime annotations?
More generally, I'm interested in understanding what is the canonical way of solving this callback/hook problem in Rust. I'm open to designs different from the one I have proposed, and if there are relevant performance concerns for various options, that would be useful to know also.
What you've created is a self-referential structure, which is problematic and not really expressible with references and lifetime annotations. See: Why can't I store a value and a reference to that value in the same struct? for the potential problems and workarounds. Its an issue here because you want to be able to mutate the BusinessLogic in the callback, but since it holds the Client, you can mutate the callback while its running, which is no good.
I would instead suggest that the callback has full ownership of the BusinessLogic which does not directly reference the Client:
use rand::Rng;
struct Client {
callback: Box<dyn FnMut(i64)>,
}
impl Client {
fn do_thing(&mut self) {
let value = rand::thread_rng().gen();
(self.callback)(value);
}
}
struct BusinessLogic {
value: Option<i64>,
}
fn main() {
let mut bl = BusinessLogic {
value: None
};
let mut client = Client {
callback: Box::new(move |value| {
bl.value = Some(value);
})
};
client.do_thing();
println!("Hello, world!");
}
if you need the subscriber to have backwards communication to the Client, you can pass an additional parameter that the callback can mutate, or simply do it via return value
if you need more complicated communication from the Client to the callback, either send a Message enum as the argument, or make the callback a custom trait instead of just FnMut with additional methods
if you need a single BusinessLogic to operate from multiple Clients use Arc+Mutex to allow shared ownership
I have a messaging system and want to solve everything generically.
Messages can be sent to entities and the entities can handle the messages.
// There are many messages that implement this trait
trait Message {
type Response;
}
// Messages can be sent to 'entities'
trait Entity {
type Error;
}
// Entities can implement handlers for specific messages
trait MessageHandler<M: Message>: Entity {
fn handle(
&mut self,
message: M,
) -> Result<M::Response, Self::Error>;
}
This would be implemented like so:
struct SimpleEntity;
impl Entity for SimpleEntity {
type Error = ();
}
struct SimpleMessage;
impl Message for SimpleMessage {
type Response = ();
}
impl MessageHandler<SimpleMessage> for SimpleEntity {
fn handle(
&mut self,
message: SimpleMessage,
) -> Result<(), ()> {
Ok(())
}
}
All entities are stored in a system. The system can only store entities of 1 type. For every message handler that the type has, there should be a send_message function that takes the message generically.
I imagine it could look like this:
// A message system for one type of entity. This is an example. Normally there's all kinds of async multithreaded stuff here
struct MessageSystem<E: Entity> {
handlers: Vec<E>,
}
// For every message handler, we want to implement the send_message function
impl<M: Message, MH: MessageHandler<M>> MessageSystem<MH> {
pub fn send_message(&mut self, entity_id: (), message: M) -> Result<M::Response, MH::Error> {
unimplemented!();
}
}
This could then be used like so:
// Example usage
fn main() {
let mut system = MessageSystem { handlers: vec![SimpleEntity] };
system.send_message((), SimpleMessage).unwrap();
}
However, this gives a compile error in the impl block for the send_message function:
error[E0207]: the type parameter `M` is not constrained by the impl trait, self type, or predicates
--> src/lib.rs:25:6
|
25 | impl<M: Message, MH: MessageHandler<M>> MessageSystem<MH> {
| ^ unconstrained type parameter
error: aborting due to previous error
For more information about this error, try `rustc --explain E0207`.
Link to playground
How could I make this work?
The goal is to have these different message structs, have them be handled by a handler that an entity can implement and send the messages to the entities via the system struct.
An obvious thing would be to make the message an associated type in the MessageHandler trait, but then you can't implement multiple versions of it for an entity.
Since M is generic to send_message but not the MessageSystem trait itself, move it to the send_message function, and move the trait bound to the method.
impl<MH: Entity> MessageSystem<MH> {
pub fn send_message<M>(&mut self, entity_id: (), message: M) -> Result<M::Response, MH::Error>
where
M: Message,
MH: MessageHandler<M>,
{
unimplemented!();
}
}
Playground link
Your original error happens because you have a generic parameter that the trait doesn't use, which means it's vague and the correct impl can't be chosen.
First of all I'm new with Rust :-)
The problem:
I want to create a module called RestServer that contain the methods ( actix-web ) to add routes and start the server.
struct Route
{
url: String,
request: String,
handler: Box<dyn Fn(HttpRequest) -> HttpResponse>
}
impl PartialEq for Route {
fn eq(&self, other: &Self) -> bool {
self.url == other.url
}
}
impl Eq for Route {}
impl Hash for Route {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.url.hash(hasher);
}
}
this is the route structure, this structure containe the the route url, the request type ( GET, POST etc ) and hanlder is the function that have to catch the request and return a HTTPResponse
pub struct RestServer
{
scopes: HashMap<String, Rc<HashSet<Route>>>,
routes: HashSet<Route>,
host: String,
}
impl RestServer {
pub fn add_route(self, req: &str, funct: impl Fn(HttpRequest) -> HttpResponse + 'static,
route: &str, scope: Option<&str>) -> RestServer
{
let mut routes_end = self.routes;
let mut scopes_end = self.scopes;
let url = self.host;
let route = Route {
url: String::from(route),
request: String::from(req),
handler: Box::new(funct)
};
if let Some(x) = scope {
if let Some(y) = scopes_end.get(x) {
let mut cloned_y = Rc::clone(y);
cloned_y.insert(route);
scopes_end.insert(String::from(x), cloned_y);
}else {
let mut hash_scopes = HashSet::new();
hash_scopes.insert(route);
scopes_end.insert(String::from(x), Rc::new(hash_scopes));
}
} else {
routes_end.insert(route);
}
RestServer {
scopes: scopes_end,
routes: routes_end,
host: String::from(url)
}
}
the latest code is the implementation of RestServer.
The most important part is the add_route function, this function receive as paramente the route that is a string, the function handler, the request string and the scope.
First i create the route object.
I check if the scope exist into the HashMap, if yes i have to take the actual scope and update the HashSet.
When i build the code i get the following error
error[E0596]: cannot borrow data in an `Rc` as mutable
--> interface/src/rest/mod.rs:60:17
|
60 | cloned_y.insert(route);
| ^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not
implemented for `std::rc::Rc<std::collections::HashSet<rest::Route>>`
I know that the compiler give me some help but honestly i have no idea how to do that or if i can do with some easy solution.
After a large search in google i found a solution in RefCell, but is not so much clear
Thanks in advance for your help
You cannot borrow a reference-counting pointer as mutable; this is because one of the guarantees it provides is only possible if the structure is read-only.
You can, however, get around it, but it will require some signature changes.
Enter interior mutability
Interior mutability is a concept you may know from other programming languages in the form of mutexes, atomics and synchronization primitives. In practice, those structures allow you to temporarily guarantee that you are the only accessor of a given variable.
In Rust, this is particularly good, as it allows us to extract a mutable reference to an interior member from a structure that only requires immutable references to itself to function. Perfect to fit in Rc.
Depending on what you need for your needs, you will find the Cell and RefCell structures to be exactly what you need for this. These are not thread-safe, but then again, neither is Rc so it's not exactly a deal-breaker.
In practice, it works very simply:
let data = Rc::new(RefCell::new(true));
{
let mut reference = data.borrow_mut();
*reference = false;
}
println!("{:?}", data);
playground
(If you ever want the threaded versions, Arc replaces Rc and Mutex or RwLock replaces Cell/RefCell)
I'm attempting to send a request off with Hyper, then deserialize it via JSON through Serde, but I cannot seem to wrap my head around futures and I'm receiving type mismatch errors stating expected (), found struct [put some odd struct here]. Nor can I wrap my head around the incredibly long and confusing error messages they spit out on each change. Here's my code:
extern crate futures;
extern crate hyper;
extern crate serde;
extern crate serde_json;
use futures::{
Future,
Stream,
future
};
use hyper::{
Body,
Client,
Response,
StatusCode,
Uri,
client::HttpConnector,
};
use serde::{ Deserialize };
use std::error::{ Error };
enum JsonError
{
RequestError(hyper::Error),
ResponseError(StatusCode),
DeserializeError(serde_json::Error),
}
fn get_json
<'t, T, F>
(client: &Client<HttpConnector>, uri: Uri)
-> impl Future<Item = T, Error = JsonError>
where
T : Deserialize<'t>
{
let f = client
.get(uri)
.map(|response|
{
let (parts, body) = response.into_parts();
if parts.status.is_success()
{
body
.fold(
vec![],
|mut accum, chunk|
{
accum.extend_from_slice(&*chunk);
Ok(accum)
}
)
.map(|v|
{
serde_json::from_slice::<T>(&v)
.map_err(|err| JsonError::DeserializeError(err))
})
}
future::err(JsonError::ResponseError(parts.status))
})
.map_err(|err| JsonError::RequestError(err));
return f;
}
I am completely lost and I think any advice could help at this point.
You have multiple errors stemming from logic issues when chaining futures. I have fixed its implementation, and it is available on the playground but I would strongly recommend you walk with me through what I changed.
But first, a recurring trend in your code:
#1: Future::map()'s return type
Future::map() allows you to change a future result of type T into type R, and it does so with the assumption that the transformation cannot fail (i.e. Fn(T) -> R). You've used map() multiple times in your code while returning either another Future, or a Result. Both are incorrect.
For Future chaining, and_then() allows you to perform the mapping fn(T) -> IntoFuture<Item = R> with error types remaining the same
For Result, convert them via future::result() into an already-executed future so you can also and_then()
#2: Errors
Errors do not convert themselves, especially not if you do not define their conversion methods. For this purpose, I've implemented From<hyper::Error> for your error type:
impl From<hyper::Error> for JsonError {
fn from(s: hyper::Error) -> JsonError {
JsonError::RequestError(s)
}
}
This allows you to use into() wherever the typechecker notices that it is possible to do so.
Do note, however, that because of hyper's own response type, err_into() gets the type checker confused, hence the explicit map(|r| r.into()).
#3: Deserialize and lifetimes
Deserialize<'t> is not the exact trait you were looking for. This trait implies that the entire object needs to live for lifetime 't. In a non-futures world, this would probably pass, but not in this case where the return object needs to be owned (hence the lifetime error you are getting).
for<'t> Deserialize<'t> is a completely different beast, and tells the compiler that this trait will have a lifetime 't, but will then be an owned object, or in other words, that the slice used to create the object will need to live for lifetime 't, not the entire returned object. Just what we need!
An additional nitpick: you really ought to separate the response parsing from the HTTP extraction in this function. As it stands right now, if I make a request over HTTPS, I will not be able to use your get_json() function, as my connector for hyper would then be TlsConnector<HttpConnector>. Problematic ;-)
The code:
use futures::{future, Future, Stream};
use hyper::{client::HttpConnector, Client, StatusCode, Uri};
use serde::Deserialize;
enum JsonError {
RequestError(hyper::Error),
ResponseError(StatusCode),
DeserializeError(serde_json::Error),
}
impl From<hyper::Error> for JsonError {
fn from(s: hyper::Error) -> JsonError {
JsonError::RequestError(s)
}
}
fn get_json<T, F>(
client: &Client<HttpConnector>,
uri: Uri,
) -> impl Future<Item = T, Error = JsonError>
where
T: for<'t> Deserialize<'t> + 'static,
{
client.get(uri).map_err(|e| e.into()).and_then(
|response| -> Box<dyn Future<Item = T, Error = JsonError>> {
let (parts, body) = response.into_parts();
match parts.status.is_success() {
true => Box::new(body
.map_err(|e| e.into())
.fold(
vec![],
|mut accum, chunk| -> Box<dyn Future<Item = Vec<u8>, Error = JsonError>>
{
accum.extend_from_slice(&*chunk);
Box::new(future::ok(accum))
}
)
.and_then(|v|
{
future::result(serde_json::from_slice::<T>(&v))
.map_err(|err| JsonError::DeserializeError(err))
})),
false => Box::new(future::err(JsonError::ResponseError(parts.status)))
}
},
)
}