1use core::{
2 pin::Pin,
3 task::{Context, Poll},
4};
5
6#[must_use = "Futures do nothing unless you await them"]
10#[derive(Debug)]
11pub struct JoinArray<F, const N: usize>
12where
13 F: Future,
14{
15 futures: [F; N],
16 outputs: Option<[Option<F::Output>; N]>,
17}
18
19impl<F, const N: usize> JoinArray<F, N>
20where
21 F: Future,
22{
23 #[inline]
25 pub const fn new(futures: [F; N]) -> Self {
26 Self { futures, outputs: Some([const { None }; N]) }
27 }
28}
29
30impl<F: Future, const N: usize> Future for JoinArray<F, N>
31where
32 F: Future,
33{
34 type Output = [F::Output; N];
35
36 #[inline]
37 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
38 let JoinArray { futures, outputs } = unsafe { self.get_unchecked_mut() };
40 let Some(outputs_mut) = outputs else {
41 #[expect(
42 clippy::panic,
43 reason = "Compiler will probably remove this branch as long as the user don't mess up"
44 )]
45 {
46 panic!("Can't poll `JoinArray` again after completion");
47 }
48 };
49 let mut is_finished = true;
50 for (future, result) in futures.iter_mut().zip(outputs_mut) {
51 if result.is_some() {
52 continue;
53 }
54 let pinned = unsafe { Pin::new_unchecked(future) };
56 if let Poll::Ready(output) = pinned.poll(cx) {
57 *result = Some(output);
58 } else {
59 is_finished = false;
60 }
61 }
62 if is_finished && let Some(array) = outputs.take() {
63 #[expect(clippy::unwrap_used, reason = "Compiler removes this branch")]
64 return Poll::Ready(array.map(|el| el.unwrap()));
65 }
66 Poll::Pending
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use crate::misc::JoinArray;
73 use core::future::ready;
74
75 #[wtx::test]
76 async fn polls_array() {
77 assert_eq!(JoinArray::new([ready(1), ready(2)]).await, [1, 2]);
78 }
79}