@@ -305,10 +305,33 @@ fn and_then_or_clear<T, U>(opt: &mut Option<T>, f: impl FnOnce(&mut T) -> Option
305
305
x
306
306
}
307
307
308
+ /// Marks the two generic parameters of Chain as sufficiently equal that their values can be swapped
309
+ ///
310
+ /// # Safety
311
+ ///
312
+ /// This would be trivially safe if both types were identical, including lifetimes.
313
+ /// However we can't specify bounds like that and it would be overly restrictive since it's not
314
+ /// uncommon for borrowing iterators to have slightly different lifetimes.
315
+ ///
316
+ /// We can relax this by only requiring that the base struct type is the same while ignoring
317
+ /// lifetime parameters as long as
318
+ /// * the actual runtime lifespan of the values is capped by the shorter of the two lifetimes
319
+ /// * all invoked trait methods (and drop code) monomorphize down to the same code
308
320
#[ rustc_unsafe_specialization_marker]
309
- trait SymmetricalArms { }
310
-
311
- impl < A > SymmetricalArms for Chain < A , A > { }
321
+ unsafe trait SymmetricalModuloLifetimes { }
322
+
323
+ /// Safety:
324
+ /// * <A, A> ensures that the basic type is the same
325
+ /// * actual lifespan of the values is capped by the combined lifetime of Chain's fields as long as
326
+ /// there is no way to destructure Chain into. I.e. Chain must not implement `SourceIter`,
327
+ /// `into_parts(self)` or similar methods.
328
+ /// * we rely on the language currently having no mechanism that would allow lifetime-dependent
329
+ /// code paths. Specialization forbids `where T: 'static` and similar bounds (modulo the exposed
330
+ /// `#[rustc_unsafe_specialization_marker]` traits).
331
+ /// And any trait depending on `Any` would have to be 'static in *both* arms to make a useful Chain.
332
+ /// This is only true as long as *all* impls on `Chain` have the same bounds for A and B,
333
+ /// which currently is the case.
334
+ unsafe impl < A > SymmetricalModuloLifetimes for Chain < A , A > { }
312
335
313
336
trait SpecChain : Iterator {
314
337
fn next ( & mut self ) -> Option < Self :: Item > ;
@@ -329,13 +352,13 @@ impl<A, B> SpecChain for Chain<A, B>
329
352
where
330
353
A : Iterator ,
331
354
B : Iterator < Item = A :: Item > ,
332
- Self : SymmetricalArms ,
355
+ Self : SymmetricalModuloLifetimes ,
333
356
{
334
357
#[ inline]
335
358
fn next ( & mut self ) -> Option < A :: Item > {
336
359
let mut result = and_then_or_clear ( & mut self . a , Iterator :: next) ;
337
360
if result. is_none ( ) {
338
- // SAFETY: SymmetricalArms guarantees that A and B are the same type.
361
+ // SAFETY: SymmetricalModuloLifetimes guarantees that A and B are safe to swap
339
362
unsafe { mem:: swap ( & mut self . a , & mut * ( & mut self . b as * mut _ as * mut Option < A > ) ) } ;
340
363
result = and_then_or_clear ( & mut self . a , Iterator :: next) ;
341
364
}
0 commit comments