@@ -157,29 +157,50 @@ impl IntoIterator for Array {
157
157
// to the inner slice, then we can:
158
158
// - Drop the elements outside our inner view,
159
159
// - Move out the elements inside out inner view.
160
+ // - Drop the rest of the elements when we're dropped
160
161
// Otherwise, we clone everything.
161
-
162
- unsafe {
163
- let mut inner = transmute :: < Rc < [ RichTerm ] > , Rc < [ ManuallyDrop < RichTerm > ] > > ( self . inner ) ;
164
- let idx = self . start ;
165
- let end = self . end ;
166
-
167
- if let Some ( slice) = Rc :: get_mut ( & mut inner) {
168
- for term in & mut slice[ ..idx] {
162
+ //
163
+ // If we start as a shared reference, we could become the only reference
164
+ // later, but we clone everything anyways, so we don't end up in an
165
+ // invalid in-between state where some terms have been freed manually
166
+ // and others haven't.
167
+
168
+ let inner = if Rc :: strong_count ( & self . inner ) != 1 || Rc :: weak_count ( & self . inner ) != 0 {
169
+ IntoIterInner :: Shared ( self . inner )
170
+ } else {
171
+ unsafe {
172
+ let mut inner =
173
+ transmute :: < Rc < [ RichTerm ] > , Rc < [ ManuallyDrop < RichTerm > ] > > ( self . inner ) ;
174
+ let slice =
175
+ Rc :: get_mut ( & mut inner) . expect ( "non-unique Rc after checking for uniqueness" ) ;
176
+ for term in & mut slice[ ..self . start ] {
169
177
ManuallyDrop :: drop ( term)
170
178
}
171
- for term in & mut slice[ end..] {
179
+ for term in & mut slice[ self . end ..] {
172
180
ManuallyDrop :: drop ( term)
173
181
}
174
- }
175
182
176
- IntoIter { inner, idx, end }
183
+ IntoIterInner :: Owned ( inner)
184
+ }
185
+ } ;
186
+ IntoIter {
187
+ inner,
188
+ idx : self . start ,
189
+ end : self . end ,
177
190
}
178
191
}
179
192
}
180
193
194
+ enum IntoIterInner {
195
+ Shared ( Rc < [ RichTerm ] > ) ,
196
+ // This shouldn't really be an Rc. It should only ever have one reference.
197
+ // There may be a way to rewrite this once unsized locals are stabilized.
198
+ // But for now, there is no good way to repackage the inner array, so we
199
+ // keep it in an Rc.
200
+ Owned ( Rc < [ ManuallyDrop < RichTerm > ] > ) ,
201
+ }
181
202
pub struct IntoIter {
182
- inner : Rc < [ ManuallyDrop < RichTerm > ] > ,
203
+ inner : IntoIterInner ,
183
204
idx : usize ,
184
205
end : usize ,
185
206
}
@@ -191,14 +212,14 @@ impl Iterator for IntoIter {
191
212
if self . idx == self . end {
192
213
None
193
214
} else {
194
- let term = match Rc :: get_mut ( & mut self . inner ) {
195
- None => self . inner [ .. self . end ]
196
- . get ( self . idx )
197
- . cloned ( )
198
- . map ( ManuallyDrop :: into_inner ) ,
199
- Some ( slice ) => slice [ .. self . end ]
200
- . get_mut ( self . idx )
201
- . map ( |t| unsafe { ManuallyDrop :: take ( t ) } ) ,
215
+ let term = match & mut self . inner {
216
+ IntoIterInner :: Shared ( inner ) => inner. get ( self . idx ) . cloned ( ) ,
217
+ IntoIterInner :: Owned ( inner ) => unsafe {
218
+ Rc :: get_mut ( inner )
219
+ . expect ( "non-unique Rc after checking for uniqueness" )
220
+ . get_mut ( self . idx )
221
+ . map ( |t| ManuallyDrop :: take ( t ) )
222
+ } ,
202
223
} ;
203
224
204
225
self . idx += 1 ;
@@ -207,6 +228,18 @@ impl Iterator for IntoIter {
207
228
}
208
229
}
209
230
231
+ impl Drop for IntoIter {
232
+ fn drop ( & mut self ) {
233
+ // drop the rest of the items we haven't iterated through
234
+ if let IntoIterInner :: Owned ( inner) = & mut self . inner {
235
+ let inner = Rc :: get_mut ( inner) . expect ( "non-unique Rc after checking for uniqueness" ) ;
236
+ for term in & mut inner[ self . idx ..self . end ] {
237
+ unsafe { ManuallyDrop :: drop ( term) }
238
+ }
239
+ }
240
+ }
241
+ }
242
+
210
243
impl ExactSizeIterator for IntoIter {
211
244
fn len ( & self ) -> usize {
212
245
self . end - self . idx
0 commit comments