Skip to content

Commit 7648d42

Browse files
authored
Merge pull request #3328 from Ddystopia/main
fix: `select_slice` is unsound.
2 parents dc98d86 + 29932c2 commit 7648d42

File tree

1 file changed

+15
-20
lines changed

1 file changed

+15
-20
lines changed

embassy-futures/src/select.rs

+15-20
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl<Fut: Future, const N: usize> Future for SelectArray<Fut, N> {
237237
#[derive(Debug)]
238238
#[must_use = "futures do nothing unless you `.await` or poll them"]
239239
pub struct SelectSlice<'a, Fut> {
240-
inner: &'a mut [Fut],
240+
inner: Pin<&'a mut [Fut]>,
241241
}
242242

243243
/// Creates a new future which will select over a slice of futures.
@@ -247,31 +247,26 @@ pub struct SelectSlice<'a, Fut> {
247247
/// future that was ready.
248248
///
249249
/// If the slice is empty, the resulting future will be Pending forever.
250-
pub fn select_slice<'a, Fut: Future>(slice: &'a mut [Fut]) -> SelectSlice<'a, Fut> {
250+
pub fn select_slice<'a, Fut: Future>(slice: Pin<&'a mut [Fut]>) -> SelectSlice<'a, Fut> {
251251
SelectSlice { inner: slice }
252252
}
253253

254254
impl<'a, Fut: Future> Future for SelectSlice<'a, Fut> {
255255
type Output = (Fut::Output, usize);
256256

257-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
258-
// Safety: Since `self` is pinned, `inner` cannot move. Since `inner` cannot move,
259-
// its elements also cannot move. Therefore it is safe to access `inner` and pin
260-
// references to the contained futures.
261-
let item = unsafe {
262-
self.get_unchecked_mut()
263-
.inner
264-
.iter_mut()
265-
.enumerate()
266-
.find_map(|(i, f)| match Pin::new_unchecked(f).poll(cx) {
267-
Poll::Pending => None,
268-
Poll::Ready(e) => Some((i, e)),
269-
})
270-
};
271-
272-
match item {
273-
Some((idx, res)) => Poll::Ready((res, idx)),
274-
None => Poll::Pending,
257+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
258+
// Safety: refer to
259+
// https://users.rust-lang.org/t/working-with-pinned-slices-are-there-any-structurally-pinning-vec-like-collection-types/50634/2
260+
#[inline(always)]
261+
fn pin_iter<T>(slice: Pin<&mut [T]>) -> impl Iterator<Item = Pin<&mut T>> {
262+
unsafe { slice.get_unchecked_mut().iter_mut().map(|v| Pin::new_unchecked(v)) }
263+
}
264+
for (i, fut) in pin_iter(self.inner.as_mut()).enumerate() {
265+
if let Poll::Ready(res) = fut.poll(cx) {
266+
return Poll::Ready((res, i));
267+
}
275268
}
269+
270+
Poll::Pending
276271
}
277272
}

0 commit comments

Comments
 (0)