DelSkayn / rquickjs

High level bindings to the quickjs javascript engine
MIT License
504 stars 63 forks source link

How to declare an ArrayBuffer in async? #132

Closed stevefan1999-personal closed 1 year ago

stevefan1999-personal commented 1 year ago

I watn to implement write_all:

    #[quickjs(cloneable, rename = "TcpStream")]
    #[derive(Clone, Debug, From, Into, Deref, DerefMut, AsRef)]
    pub struct TcpStreamWrapper(Arc<Mutex<tokio::net::TcpStream>>);

    #[quickjs(rename = "TcpStream")]
    impl TcpStreamWrapper {
        // instance property getter
        #[quickjs(get, enumerable)]
        pub fn local_addr(&self) -> rquickjs::Result<SocketAddrWrapper> {
            let stream = self.0.try_lock().map_err(|_| rquickjs::Error::Unknown)?;
            Ok(stream.local_addr()?.into())
        }

        pub async fn write_all<'js>(self, buf: TypedArray<'js, u8>) -> rquickjs::Result<()> {
            self.0.lock().await.write_all(buf.as_ref()).await?;
            Ok(())
        }
    }

However it seems like it is not acceptable?

error[E0277]: the trait bound `Async<rquickjs::Method<for<'js> fn(TcpStreamWrapper, TypedArray<'js, u8>) -> impl Future<Output = Result<(), rquickjs::Error>> {TcpStreamWrapper::write_all}>>: AsFunction<'_, _, _>` is not satisfied                                                                                  
   --> src\js.rs:85:1
    |
85  | #[bind(object)]
    | ^^^^^^^^^^^^^^^ the trait `AsFunction<'_, _, _>` is not implemented for `Async<rquickjs::Method<for<'js> fn(TcpStreamWrapper, TypedArray<'js, u8>) -> impl Future<Output = Result<(), rquickjs::Error>> {TcpStreamWrapper::write_all}>>`
    |
    = help: the following other types implement trait `AsFunction<'js, A, R>`:
              <Async<F> as AsFunction<'js, (), Promised<R>>>
              <Async<F> as AsFunction<'js, (A, B), Promised<R>>>
              <Async<F> as AsFunction<'js, (A, B, D), Promised<R>>>
              <Async<F> as AsFunction<'js, (A, B, D, E), Promised<R>>>
              <Async<F> as AsFunction<'js, (A, B, D, E, G), Promised<R>>>
              <Async<F> as AsFunction<'js, (A, B, D, E, G, H), Promised<R>>>
              <Async<F> as AsFunction<'js, (A,), Promised<R>>>
              <Async<MutFn<F>> as AsFunction<'js, (), Promised<R>>>
            and 20 others
    = note: required for `Func<(&str, Async<rquickjs::Method<for<'js> fn(TcpStreamWrapper, TypedArray<'js, u8>) -> impl Future<Output = Result<(), rquickjs::Error>> {TcpStreamWrapper::write_all}>>, PhantomData<(_, _)>)>` to implement `rquickjs::IntoJs<'_>`
note: required by a bound in `rquickjs::Object::<'js>::set`
   --> C:\Users\steve\scoop\persist\rustup\.cargo\registry\src\index.crates.io-6f17d22bba15001f\rquickjs-core-0.1.7\src\value\object.rs:114:37
    |
114 |     pub fn set<K: IntoAtom<'js>, V: IntoJs<'js>>(&self, key: K, value: V) -> Result<()> {
    |                                     ^^^^^^^^^^^ required by this bound in `Object::<'js>::set`
    = note: this error originates in the attribute macro `bind` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.                                                                                                                                                                                                                                                    
warning: `rquickjs-fun` (bin "rquickjs-fun") generated 2 warnings                                                                                                                                                                                                                                                      
error: could not compile `rquickjs-fun` (bin "rquickjs-fun") due to previous error; 2 warnings emitted
DelSkayn commented 1 year ago

The problem is that the function write_all returns a future which has a lifetime of 'js , because it has taken TypedArray<'js, u8> as an argument. The current implementation requires that async callbacks to return futures which are valid for the entire lifetime of 'static.

I will look into the possibility of lifting this restriction but for now it is impossible to implement that method as an async function. You could try to implement it as a normal function which returns a future which is valid for 'static by making sure that TypedArray is not passed into the future.

Something like:

pub fn write_all<'js>(self, buf: TypedArray<'js, u8>) -> impl Future<Output = rquickjs::Result<()>> {
    // Move the data out of TypedArray
    let buf: Vec<u8> = buf.as_ref().to_owned();
    async move {
        self.0.lock().await.write_all(&buf).await?;
        Ok(())
    }
}
DelSkayn commented 1 year ago

Closing this issue as the problem is resolved and now also easily possible with the new version of the library