rnxpyke opened issue #10521:
Feature
Currently it's not possible to have a mutable reference to custom store data and something like store memory at the same time.
E.g, the following doesn't compile:fn hostfunc(mut caller: Caller<'_, CustomData>, (ptr, len): (i32, i32)) -> Box<dyn Future<Output = anyhow::Result<i32>> + Send + '_> { let buf: &mut Vec<u8> = caller.data_mut().buf; let Some(Extern::Memory(m)) = caller.get_export("memory") else { panic!() }; m.read(&caller, ptr as usize, buf)?; return Ok(0); }
By changing the struct layout of
Caller<'_, CustomData>
into something likepub struct Context<'a, T> { pub caller: Caller<'a> // caller without data pub data: &'a mut T, }
you can access the caller and data fields seperately.
The compiler is smart enough to allow mutable references to disjoint struct fields.Benefit
code like this would typecheck:
fn hostfunc(mut ctx: Context<'_, CustomData>, (ptr, len): (i32, i32)) -> Box<dyn Future<Output = anyhow::Result<i32>> + Send + '_> { let buf: &mut Vec<u8> = ctx.data.buf; let Some(Extern::Memory(m)) = caller.get_export("memory") else { panic!() }; m.read(&ctx.caller, ptr as usize, buf)?; return Ok(0); }
when copying data to or especially from wasm memory, you can skip an additional allocation that would otherwise be necessary.
Implementation
Expose
data
and the wasmCaller
/Store
as seperate (public) struct fields.Alternatives
Current workarounds to the concrete problem of copying memory would be
- allocating a dedicated buffer, copy from store -> intermediate -> data
- declare data.buf field as
Option<Vec<_>>
, andbuf.take().unwrap()
the buffer to get ownership of the value.both solutions have drawbacks.
either you introduce another allocation, or you give up on some type safety by unwrapping options.
alexcrichton commented on issue #10521:
Can you use
Memory::data_and_store_mut
to solve your use case here?
rnxpyke commented on issue #10521:
hm, I didn't see that method when speed-reading the documentation.
I think this api would also solve my allocation issues. :+1:It's kinda confusing that in the context of a host function using the
Caller
struct,data
refers to the generic type parameter T, but in theMemory
struct it's swapped, data refers to raw bytes and store to the generic type.
alexcrichton commented on issue #10521:
It's true it's unfortunate, but we're only so good at naming...
Given that it works for you though I'm going to close this, but if you have anything further feel free to post here as well
alexcrichton closed issue #10521:
Feature
Currently it's not possible to have a mutable reference to custom store data and something like store memory at the same time.
E.g, the following doesn't compile:fn hostfunc(mut caller: Caller<'_, CustomData>, (ptr, len): (i32, i32)) -> Box<dyn Future<Output = anyhow::Result<i32>> + Send + '_> { let buf: &mut Vec<u8> = caller.data_mut().buf; let Some(Extern::Memory(m)) = caller.get_export("memory") else { panic!() }; m.read(&caller, ptr as usize, buf)?; return Ok(0); }
By changing the struct layout of
Caller<'_, CustomData>
into something likepub struct Context<'a, T> { pub caller: Caller<'a> // caller without data pub data: &'a mut T, }
you can access the caller and data fields seperately.
The compiler is smart enough to allow mutable references to disjoint struct fields.Benefit
code like this would typecheck:
fn hostfunc(mut ctx: Context<'_, CustomData>, (ptr, len): (i32, i32)) -> Box<dyn Future<Output = anyhow::Result<i32>> + Send + '_> { let buf: &mut Vec<u8> = ctx.data.buf; let Some(Extern::Memory(m)) = caller.get_export("memory") else { panic!() }; m.read(&ctx.caller, ptr as usize, buf)?; return Ok(0); }
when copying data to or especially from wasm memory, you can skip an additional allocation that would otherwise be necessary.
Implementation
Expose
data
and the wasmCaller
/Store
as seperate (public) struct fields.Alternatives
Current workarounds to the concrete problem of copying memory would be
- allocating a dedicated buffer, copy from store -> intermediate -> data
- declare data.buf field as
Option<Vec<_>>
, andbuf.take().unwrap()
the buffer to get ownership of the value.both solutions have drawbacks.
either you introduce another allocation, or you give up on some type safety by unwrapping options.
Last updated: Apr 17 2025 at 10:03 UTC