So basically I have
TFn: Fn() -> TResult,
// the idea is to make this Future optiona
TResult: Future<Output = error::Result<usize>>,
I need to make a callback that is optionally async. It means that if function returns the future I want to make a block_on inside the function.
Is there any idiomatic way to do this without duplication?
Related
I have a struct Obj with a large API that I would also like to use through an Arc<RwLock<Obj>>. I have defined a struct ObjRef(Arc<RwLock<Obj>>) and want to call functions of Obj on ObjRef without needing to call .read().unwrap() or .write().unwrap() every time. (ObjRef implements Deref<Target=Arc<RwLock<Obj>>, so I only have to call objRef.read().unwrap())
I could implement all of the functions of Obj again for ObjRef in a manner like this:
impl ObjRef {
fn foo(&self, arg: Arg) -> Ret {
self.read().unwrap().foo(arg)
}
}
But doing this for every function results in a lot of boilerplate.
Implementing Deref<Target=Obj> for ObjRef does not work, because the deref implementation would be returning a reference into the RwReadLockGuard object returned by .read().unwrap() which would be dropped after deref:
impl Deref for ObjRef {
type Target = Obj;
fn deref(&self) -> &Self::Target {
let guard: RwLockReadGuard<'_, Obj> = self.0.read().unwrap();
&*guard // <- return reference to Obj referenced by guard
// guard is dropped now, reference lifetime ends
}
}
Is there a way to call the Obj Api through a low boilerplate implementation doing the locking internally?
I am thinking about introducing a trait for the Api with a default implementation of the Api and a single function to be implemented by users of the trait for getting a generic Deref<Target=Obj>, but this puts the Api under some limitations, for example using generic parameters will become much more complicated.
Is there maybe a better way, something that lets me use any Obj through a lock without explicitly calling the locking mechanism every time?
It is not possible to do this through Deref, because Deref::deref always returns a plain reference — the validity of it must only depend on the argument continuing to exist (which the borrow checker understands), not on any additional state such as "locked".
Also, adding locking generically to an interface designed without it is dangerous, because it may accidentally cause deadlocks, or allow unwanted outcomes due to doing two operations in sequence that should have been done using a single locking.
I recommend considering making your type internally an Arc<RwLock, that is,
pub struct Obj {
lock: Arc<RwLock<ObjData>>,
}
// internal helper, not public
struct ObjData {
// whatever fields Obj would have had go here
}
Then, you can define your methods once, directly on Obj, and the caller never needs to deal with the lock explicitly, but your methods can handle it exactly as needed. This is a fairly common pattern in problem domains that benefit from it, like UI objects.
If there is some reason why Obj really needs to be usable with or without the lock, then define a trait.
fn subscribe(&mut self, subscriber: Subscriber) -> Box<dyn FnMut() -> bool> {
self.callbacks.insert(subscriber);
Box::new(|| self.callbacks.remove(&subscriber))
}
How do I correct the error: returning this value requires that '1 must outlive 'static
Is it possible to do this without lifetime annotation?
subscriber is a argument in your subscribe function. It goes out of scope when that function returns - that is, its lifetime ends. You are trying to return a closure that holds a reference to subscriber, but subscriber will already be gone when that closure is called. self can go out of scope before it gets called too.
This is likely an anti-pattern. What is the purpose of the returned closure? Is it a sort of "undo action" (since it calls remove)? If so, I would just set up another function called unsubscribe that takes a Subscriber.
I have a hyper server set up more or less exactly as the third example here: https://docs.rs/hyper/0.14.16/hyper/server/index.html . My version of the handle function calls some other async functions, and everything works fine, until I try to encode some URL query params into a string in one of those async functions. My project stops compiling when I include the four lines involving the Serializer in one of the functions called by handle:
async fn broken_func(&self, param: &str) -> Result<String, Infallible> {
// ...
let mut s = url::form_urlencoded::Serializer::new(String::new());
// the presence or absence of these two lines has no effect on the bug, but
// they demonstrate how I'm trying to use the Serializer
s.append_pair("key", "value");
println!("{:?}", s.finish());
drop(s); // <-- thought this might help, but it doesn't
// ...
Ok(query_string)
}
The error I get is
generator cannot be sent between threads safely
the trait `std::marker::Sync` is not implemented for `dyn for<'r> std::ops::Fn(&'r str) -> std::borrow::Cow<'_, [u8]>`
I have no idea what this has to do with form_urlencoded::Serializer. However, I am aware that Serializer is both !Send and !Sync, but in this case I'm only using it within a single function so I don't think that should make a difference? If I remove those four lines above, it goes back to compiling.
So instead, to serialize some key/value pairs into URL query parameters, I have to use the following, which kind of seems ridiculous -- not just because this is needlessly complex for something so simple, but also because url::Url::parse_with_params uses form_urlencoded::Serializer under the hood.
let query_string = url::Url::parse_with_params(
"http://example.com",
&[("key", "value")]
)
.unwrap()
.query()
.map(|s| s.to_owned())
.unwrap();
Any idea why trying to explicitly use Serializer inside an async function causes things to break?
Caesar hit the nail on the head. The trick of putting s in a scope fixed it.
I am using Reqwest to call some APIs. I want to have a general function to parse the response as below:
async fn response_to_result(response: &Response) -> anyhow::Result<()> {
let status = response.status().as_u16();
let response_message = response.text().await?; // Move here because of response.text()
return if status == 200 {
Ok(())
} else {
Err(anyhow::anyhow!(response_message))
};
}
This is the error I got:
move occurs because `*response` has type `reqwest::Response`, which does not implement the `Copy` trait
The move occurs by calling response.text() (method definition: pub async fn text(self) -> crate::Result<String>). Normally with params we can pass by reference, but with the method I don't have any idea. Does anyone have a solution for this?
Normally with params, we can pass with reference
That's true as long as the method doesn't consume the value. text() does, since it assembles all the chunks together for you. Here's its signature:
pub async fn text(self) -> Result<String>
Note the self rather than &self. After running text(), the whole stream is consumed, a String is constructed, and it's returned to you to manage. Response does not have an internal buffer that stores all this data. It's gone once the stream is consumed; keeping track of it is the job of the caller. Future calls to text() (or bytes()) couldn't work. So text() destroys the entire Response. You can't continue to use it after calling text().
So you'll need to pass the actual value, not a borrow of it. Remove the & on Response.
If you want a (mutable) borrowing version of this, you'll need to assemble the chunks yourself with chunk(). But you probably don't want to do this. You probably just want to pass Response (without the &) and let response_to_result() consume the value like text().
The comments on closure.rs are pretty great, however I can't make it work for returning a closure from a WebAssembly library.
I have a function like this:
#[wasm_bindgen]
pub fn start_game(
start_time: f64,
screen_width: f32,
screen_height: f32,
on_render: &js_sys::Function,
on_collision: &js_sys::Function,
) -> ClosureTypeHere {
// ...
}
Inside that function I make a closure, assuming Closure::wrap is one piece of the puzzle, and copying from closure.rs):
let cb = Closure::wrap(Box::new(move |time| time * 4.2) as Box<FnMut(f64) -> f64>);
How do I return this callback from start_game and what should ClosureTypeHere be?
The idea is that start_game will create local mutable objects - like a camera, and the JavaScript side should be able to call the function Rust returns in order to update that camera.
This is a good question, and one that has some nuance too! It's worth calling out the closures example in the wasm-bindgen guide (and the section about passing closures to JavaScript) as well, and it'd be good to contribute back to that as well if necessary!
To get you started, though, you can do something like this:
use wasm_bindgen::{Closure, JsValue};
#[wasm_bindgen]
pub fn start_game(
start_time: f64,
screen_width: f32,
screen_height: f32,
on_render: &js_sys::Function,
on_collision: &js_sys::Function,
) -> JsValue {
let cb = Closure::wrap(Box::new(move |time| {
time * 4.2
}) as Box<FnMut(f64) -> f64>);
// Extract the `JsValue` from this `Closure`, the handle
// on a JS function representing the closure
let ret = cb.as_ref().clone();
// Once `cb` is dropped it'll "neuter" the closure and
// cause invocations to throw a JS exception. Memory
// management here will come later, so just leak it
// for now.
cb.forget();
return ret;
}
Above the return value is just a plain-old JS object (here as a JsValue) and we create that with the Closure type you've seen already. This will allow you to quickly return a closure to JS and you'll be able to call it from JS as well.
You've also asked about storing mutable objects and such, and that can all be done through normal Rust closures, capturing, etc. For example the declaration of FnMut(f64) -> f64 above is the signature of the JS function, and that can be any set of types such as FnMut(String, MyCustomWasmBindgenType, f64) ->
Vec<u8> if you really want. For capturing local objects you can do:
let mut camera = Camera::new();
let mut state = State::new();
let cb = Closure::wrap(Box::new(move |arg1, arg2| { // note the `move`
if arg1 {
camera.update(&arg2);
} else {
state.update(&arg2);
}
}) as Box<_>);
(or something like that)
Here the camera and state variables will be owned by the closure and dropped at the same time. More info about just closures can be found in the Rust book.
It's also worth briefly covering the memory management aspect here. In the
example above we're calling forget() which leaks memory and can be a problem if the Rust function is called many times (as it would leak a lot of memory). The fundamental problem here is that there's memory allocated on the WASM heap which the created JS function object references. This allocated memory in theory needs to be deallocated whenever the JS function object is GC'd, but we have no way of knowing when that happens (until WeakRef exists!).
In the meantime we've chosen an alternate strategy. The associated memory is
deallocated whenever the Closure type itself is dropped, providing
deterministic destruction. This, however, makes it difficult to work with as we need to figure out manually when to drop the Closure. If forget doesn't work for your use case, some ideas for dropping the Closure are:
First, if it's a JS closure only invoked once, then you can use Rc/RefCell
to drop the Closure inside the the closure itself (using some interior
mutability shenanigans). We should also eventually
provide native support
for FnOnce in wasm-bindgen as well!
Next, you can return an auxiliary JS object to Rust which has a manual free
method. For example a #[wasm_bindgen]-annotated wrapper. This wrapper would
then need to be manually freed in JS when appropriate.
If you can get by, forget is by far the easiest thing to do for
now, but this is definitely a pain point! We can't wait for WeakRef to exist :)
As far as I understand from documentation, it isn't supposed to export Rust closures, they only might be passed over as parameters to imported JS functions, but all this happens in Rust code.
https://rustwasm.github.io/wasm-bindgen/reference/passing-rust-closures-to-js.html#passing-rust-closures-to-imported-javascript-functions
I made couple of experiments, and when a Rust function returns the mentioned 'Closure' type, compiler throws exception: the trait wasm_bindgen::convert::IntoWasmAbi is not implemented for wasm_bindgen::prelude::Closure<(dyn std::ops::FnMut() -> u32 + 'static)>
In all examples, closures are wrapped into an arbitrary sctuct, but after that you already can't call this on JS side.