fn main() {
let boxed = <Foo as MyTrait>::call();
assert_eq!(pollster::block_on(boxed), "hello world");
}
Although we already have async-trait crate, it allocates on every async fn called. This crate can bypass the overhead. The test function is at lib.rs
I hope someone can tell me whether this implementation is sound, thanks!
This isn't ok because you're using u8 to store the futures. A future might contain uninitialized memory (e.g. padding), but an u8 is not allowed to be uninitialized. To see this, try this code:
test test::main ... error: Undefined Behavior: constructing invalid value at .0[0]: encountered uninitialized bytes
--> src/specified_box.rs:43:17
|
43 | buffer,
| ^^^^^^ constructing invalid value at .0[0]: encountered uninitialized bytes
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `specified_box::SpecifiedBox::<buffer_sel::Align4<12>, dyn std::future::Future<Output = std::string::String>>::new::<[async fn body@src/lib.rs:46:31: 54:6]>` at src/specified_box.rs:43:17: 43:23
note: inside `<test::Foo as test::MyTrait>::call`
--> src/lib.rs:66:13
|
66 | Self::Output::new(call())
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: inside `test::main`
--> src/lib.rs:72:21
|
72 | let boxed = <Foo as MyTrait>::call();
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: inside closure
--> src/lib.rs:71:15
|
70 | #[test]
| ------- in this procedural macro expansion
71 | fn main() {
| ^
= note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error
Thanks a lot! I fixed it with MaybeUinit<u8> as you said, now it passed Miri.
But I have a question, [u8] here can only be read with the type who initializes it, so theoretically what the padding bytes exactly are doesn't matter, will it still be UB? Or is reading uninitialized bytes illegal, no matter what I'm going to do with them?
It's legal in the same sense as unreachable_unchecked is legal. And means essentially the same thing: code which compiler can assume wouldn't be executed and which it may safely remove.
I prefer to use unreachable_unchecked, though, it declares my intent much more precisely.