From fd5f8d90c5cc593e6aa43a978a1498f88558cc39 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Thu, 11 Jul 2013 00:23:59 -0400 Subject: [PATCH 1/4] iterator: add DoubleEndedIterator concept This implements the trait for vector iterators, replacing the reverse iterator types. The methods will stay, for implementing the future reverse Iterable traits and convenience. This can also be trivially implemented for circular buffers and other variants of arrays like strings and `SmallIntMap`/`SmallIntSet`. The `DoubleEndedIterator` trait will allow for implementing algorithms like in-place reverse on generic mutable iterators. The naming (`Range` vs. `Iterator`, `Bidirectional` vs. `DoubleEnded`) can be bikeshedded in the future. --- src/libstd/iterator.rs | 52 +++++++++++++++++++++++++++++ src/libstd/vec.rs | 74 +++++++++++++++++++----------------------- 2 files changed, 85 insertions(+), 41 deletions(-) diff --git a/src/libstd/iterator.rs b/src/libstd/iterator.rs index 4f5f1bd85c73f..a17fea24b2c23 100644 --- a/src/libstd/iterator.rs +++ b/src/libstd/iterator.rs @@ -47,6 +47,49 @@ pub trait Iterator { fn size_hint(&self) -> (uint, Option) { (0, None) } } +/// A range iterator able to yield elements from both ends +pub trait DoubleEndedIterator: Iterator { + /// Yield an element from the end of the range, returning `None` if the range is empty. + fn next_back(&mut self) -> Option; +} + +/// Iterator adaptors provided for every `DoubleEndedIterator` implementation. +/// +/// In the future these will be default methods instead of a utility trait. +pub trait DoubleEndedIteratorUtil { + /// Flip the direction of the iterator + fn invert(self) -> InvertIterator; +} + +/// Iterator adaptors provided for every `DoubleEndedIterator` implementation. +/// +/// In the future these will be default methods instead of a utility trait. +impl> DoubleEndedIteratorUtil for T { + /// Flip the direction of the iterator + #[inline] + fn invert(self) -> InvertIterator { + InvertIterator{iter: self} + } +} + +/// An double-ended iterator with the direction inverted +// FIXME #6967: Dummy A parameter to get around type inference bug +pub struct InvertIterator { + priv iter: T +} + +impl> Iterator for InvertIterator { + #[inline] + fn next(&mut self) -> Option { self.iter.next_back() } + #[inline] + fn size_hint(&self) -> (uint, Option) { self.iter.size_hint() } +} + +impl> DoubleEndedIterator for InvertIterator { + #[inline] + fn next_back(&mut self) -> Option { self.iter.next() } +} + /// Iterator adaptors provided for every `Iterator` implementation. The adaptor objects are also /// implementations of the `Iterator` trait. /// @@ -1474,4 +1517,13 @@ mod tests { let xs = [-3, 0, 1, 5, -10]; assert_eq!(*xs.iter().min_by(|x| x.abs()).unwrap(), 0); } + + #[test] + fn test_invert() { + let xs = [2, 4, 6, 8, 10, 12, 14, 16]; + let mut it = xs.iter(); + it.next(); + it.next(); + assert_eq!(it.invert().transform(|&x| x).collect::<~[int]>(), ~[16, 14, 12, 10, 8, 6]); + } } diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index 825dc4cc187d8..5576b4ad7043e 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -18,7 +18,7 @@ use container::{Container, Mutable}; use cmp; use cmp::{Eq, TotalEq, TotalOrd, Ordering, Less, Equal, Greater}; use clone::Clone; -use iterator::{FromIterator, Iterator, IteratorUtil}; +use iterator::*; use kinds::Copy; use libc::c_void; use num::Zero; @@ -760,12 +760,7 @@ impl<'self,T> ImmutableVector<'self, T> for &'self [T] { } #[inline] fn rev_iter(self) -> VecRevIterator<'self, T> { - unsafe { - let p = vec::raw::to_ptr(self); - VecRevIterator{ptr: p.offset(self.len() - 1), - end: p.offset(-1), - lifetime: cast::transmute(p)} - } + self.iter().invert() } /// Returns an iterator over the subslices of the vector which are @@ -1717,13 +1712,9 @@ impl<'self,T> MutableVector<'self, T> for &'self mut [T] { } } + #[inline] fn mut_rev_iter(self) -> VecMutRevIterator<'self, T> { - unsafe { - let p = vec::raw::to_mut_ptr(self); - VecMutRevIterator{ptr: p.offset(self.len() - 1), - end: p.offset(-1), - lifetime: cast::transmute(p)} - } + self.mut_iter().invert() } /** @@ -2083,17 +2074,17 @@ macro_rules! iterator { priv lifetime: $elem // FIXME: #5922 } };*/ - (impl $name:ident -> $elem:ty, $step:expr) => { - // could be implemented with &[T] with .slice(), but this avoids bounds checks + (impl $name:ident -> $elem:ty) => { impl<'self, T> Iterator<$elem> for $name<'self, T> { #[inline] fn next(&mut self) -> Option<$elem> { + // could be implemented with slices, but this avoids bounds checks unsafe { if self.ptr == self.end { None } else { let old = self.ptr; - self.ptr = self.ptr.offset($step); + self.ptr = self.ptr.offset(1); Some(cast::transmute(old)) } } @@ -2101,11 +2092,7 @@ macro_rules! iterator { #[inline] fn size_hint(&self) -> (uint, Option) { - let diff = if $step > 0 { - (self.end as uint) - (self.ptr as uint) - } else { - (self.ptr as uint) - (self.end as uint) - }; + let diff = (self.end as uint) - (self.ptr as uint); let exact = diff / size_of::<$elem>(); (exact, Some(exact)) } @@ -2113,6 +2100,25 @@ macro_rules! iterator { } } +macro_rules! double_ended_iterator { + (impl $name:ident -> $elem:ty) => { + impl<'self, T> DoubleEndedIterator<$elem> for $name<'self, T> { + #[inline] + fn next_back(&mut self) -> Option<$elem> { + // could be implemented with slices, but this avoids bounds checks + unsafe { + if self.end == self.ptr { + None + } else { + self.end = self.end.offset(-1); + Some(cast::transmute(self.end)) + } + } + } + } + } +} + //iterator!{struct VecIterator -> *T, &'self T} /// An iterator for iterating over a vector. pub struct VecIterator<'self, T> { @@ -2120,16 +2126,9 @@ pub struct VecIterator<'self, T> { priv end: *T, priv lifetime: &'self T // FIXME: #5922 } -iterator!{impl VecIterator -> &'self T, 1} - -//iterator!{struct VecRevIterator -> *T, &'self T} -/// An iterator for iterating over a vector in reverse. -pub struct VecRevIterator<'self, T> { - priv ptr: *T, - priv end: *T, - priv lifetime: &'self T // FIXME: #5922 -} -iterator!{impl VecRevIterator -> &'self T, -1} +iterator!{impl VecIterator -> &'self T} +double_ended_iterator!{impl VecIterator -> &'self T} +pub type VecRevIterator<'self, T> = InvertIterator<&'self T, VecIterator<'self, T>>; //iterator!{struct VecMutIterator -> *mut T, &'self mut T} /// An iterator for mutating the elements of a vector. @@ -2138,16 +2137,9 @@ pub struct VecMutIterator<'self, T> { priv end: *mut T, priv lifetime: &'self mut T // FIXME: #5922 } -iterator!{impl VecMutIterator -> &'self mut T, 1} - -//iterator!{struct VecMutRevIterator -> *mut T, &'self mut T} -/// An iterator for mutating the elements of a vector in reverse. -pub struct VecMutRevIterator<'self, T> { - priv ptr: *mut T, - priv end: *mut T, - priv lifetime: &'self mut T // FIXME: #5922 -} -iterator!{impl VecMutRevIterator -> &'self mut T, -1} +iterator!{impl VecMutIterator -> &'self mut T} +double_ended_iterator!{impl VecMutIterator -> &'self mut T} +pub type VecMutRevIterator<'self, T> = InvertIterator<&'self mut T, VecMutIterator<'self, T>>; /// An iterator that moves out of a vector. pub struct VecConsumeIterator { From 00da76dfbe3aba8c1ec399ecfa0eae4b4ba6885e Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 10 Jul 2013 15:46:03 -0400 Subject: [PATCH 2/4] vec: rm inline(never) hack just avoid giving an inline hint in the first place --- src/libstd/vec.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index 5576b4ad7043e..38212d0f29f11 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -1136,7 +1136,6 @@ impl OwnedVector for ~[T] { * * * n - The number of elements to reserve space for */ - #[inline] #[cfg(stage0)] fn reserve(&mut self, n: uint) { // Only make the (slow) call into the runtime if we have to @@ -1170,7 +1169,6 @@ impl OwnedVector for ~[T] { * * * n - The number of elements to reserve space for */ - #[inline] #[cfg(not(stage0))] fn reserve(&mut self, n: uint) { // Only make the (slow) call into the runtime if we have to @@ -1228,21 +1226,12 @@ impl OwnedVector for ~[T] { let repr: **raw::VecRepr = transmute(&mut *self); let fill = (**repr).unboxed.fill; if (**repr).unboxed.alloc <= fill { - // need more space - reserve_no_inline(self); + let new_len = self.len() + 1; + self.reserve_at_least(new_len); } self.push_fast(t); } - - // this peculiar function is because reserve_at_least is very - // large (because of reserve), and will be inlined, which - // makes push too large. - #[inline(never)] - fn reserve_no_inline(v: &mut ~[T]) { - let new_len = v.len() + 1; - v.reserve_at_least(new_len); - } } // This doesn't bother to make sure we have space. From 1d08ceb0439e1d09cb2285578872df53ad7eeb8c Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Wed, 10 Jul 2013 09:56:08 +1000 Subject: [PATCH 3/4] Fix comment indenting properly for Vim files. Indentation now works correctly on subsequent lines of a multi-line comment, whether there are leaders (` * `) or not. (Formerly it was incorrectly doing a two-space indent if there was no leader.) By default, this no longer puts a ` * ` leader on `/*!` comments, as that appears to be the current convention in the Rust source code, but that can easily be re-enabled if desired: let g:rust_bang_comment_leader = 1 --- src/etc/vim/ftplugin/rust.vim | 15 +++++++++++++-- src/etc/vim/indent/rust.vim | 18 ++++++++++++++---- src/etc/vim/syntax/rust.vim | 6 ++++-- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/etc/vim/ftplugin/rust.vim b/src/etc/vim/ftplugin/rust.vim index f329dd6ce02ac..063b1152cb2c6 100644 --- a/src/etc/vim/ftplugin/rust.vim +++ b/src/etc/vim/ftplugin/rust.vim @@ -1,14 +1,25 @@ " Vim syntax file " Language: Rust " Maintainer: Chris Morgan -" Last Change: 2013 Jul 6 +" Last Change: 2013 Jul 10 if exists("b:did_ftplugin") finish endif let b:did_ftplugin = 1 -setlocal comments=s1:/*,mb:*,ex:*/,:///,://!,:// +" The rust source code at present seems to typically omit a leader on /*! +" comments, so we'll use that as our default, but make it easy to switch. +" This does not affect indentation at all (I tested it with and without +" leader), merely whether a leader is inserted by default or not. +if exists("g:rust_bang_comment_leader") && g:rust_bang_comment_leader == 1 + " Why is the `,s0:/*,mb:\ ,ex:*/` there, you ask? I don't understand why, + " but without it, */ gets indented one space even if there were no + " leaders. I'm fairly sure that's a Vim bug. + setlocal comments=s1:/*,mb:*,ex:*/,s0:/*,mb:\ ,ex:*/,:///,://!,:// +else + setlocal comments=s0:/*!,m:\ ,ex:*/,s1:/*,mb:*,ex:*/,:///,://!,:// +endif setlocal commentstring=//%s setlocal formatoptions-=t formatoptions+=croqnlj diff --git a/src/etc/vim/indent/rust.vim b/src/etc/vim/indent/rust.vim index 55fceb96af3fb..806f8519f0a2c 100644 --- a/src/etc/vim/indent/rust.vim +++ b/src/etc/vim/indent/rust.vim @@ -66,12 +66,23 @@ function GetRustIndent(lnum) " Starting assumption: cindent (called at the end) will do it right " normally. We just want to fix up a few cases. + let line = getline(a:lnum) + if has('syntax_items') - if synIDattr(synID(a:lnum, 1, 1), "name") == "rustString" + let synname = synIDattr(synID(a:lnum, 1, 1), "name") + if synname == "rustString" " If the start of the line is in a string, don't change the indent return -1 - elseif synIDattr(synID(a:lnum, 1, 1), "name") =~ "\\(Comment\\|Todo\\)" - \ && getline(a:lnum) !~ "^\\s*/\\*" + elseif synname =~ "\\(Comment\\|Todo\\)" + \ && line !~ "^\\s*/\\*" " not /* opening line + if synname =~ "CommentML" " multi-line + if line !~ "^\\s*\\*" && getline(a:lnum - 1) =~ "^\\s*/\\*" + " This is (hopefully) the line after a /*, and it has no + " leader, so the correct indentation is that of the + " previous line. + return GetRustIndent(a:lnum - 1) + endif + endif " If it's in a comment, let cindent take care of it now. This is " for cases like "/*" where the next line should start " * ", not " "* " as the code below would otherwise cause for module scope @@ -114,7 +125,6 @@ function GetRustIndent(lnum) " start with these two main cases (square brackets and not returning to " column zero) - let line = getline(a:lnum) call cursor(a:lnum, 1) if searchpair('{\|(', '', '}\|)', 'nbW') == 0 if searchpair('\[', '', '\]', 'nbW') == 0 diff --git a/src/etc/vim/syntax/rust.vim b/src/etc/vim/syntax/rust.vim index dfcdb11e7682a..3f654ea903063 100644 --- a/src/etc/vim/syntax/rust.vim +++ b/src/etc/vim/syntax/rust.vim @@ -117,9 +117,9 @@ syn match rustFloat display "\<[0-9][0-9_]*\.[0-9_]\+\%([eE][+-]\=[0-9 syn match rustLifetime display "\'\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*" syn match rustCharacter "'\([^'\\]\|\\\(['nrt\\\"]\|x\x\{2}\|u\x\{4}\|U\x\{8}\)\)'" -syn region rustComment start="/\*" end="\*/" contains=rustTodo +syn region rustCommentML start="/\*" end="\*/" contains=rustTodo syn region rustComment start="//" skip="\\$" end="$" contains=rustTodo keepend -syn region rustCommentDoc start="/\*\%(!\|\*/\@!\)" end="\*/" contains=rustTodo +syn region rustCommentMLDoc start="/\*\%(!\|\*/\@!\)" end="\*/" contains=rustTodo syn region rustCommentDoc start="//[/!]" skip="\\$" end="$" contains=rustTodo keepend syn keyword rustTodo contained TODO FIXME XXX NB NOTE @@ -151,7 +151,9 @@ hi def link rustModPath Include hi def link rustModPathSep Delimiter hi def link rustFuncName Function hi def link rustFuncCall Function +hi def link rustCommentMLDoc rustCommentDoc hi def link rustCommentDoc SpecialComment +hi def link rustCommentML rustComment hi def link rustComment Comment hi def link rustAssert PreCondit hi def link rustFail PreCondit From 6e75f2de9c40e4107069a68c2a7b0cc7dae2b078 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Wed, 10 Jul 2013 15:58:32 +1000 Subject: [PATCH 4/4] Fix Vim indentation for inline closures. --- src/etc/vim/indent/rust.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etc/vim/indent/rust.vim b/src/etc/vim/indent/rust.vim index 806f8519f0a2c..ae3ca403aedd8 100644 --- a/src/etc/vim/indent/rust.vim +++ b/src/etc/vim/indent/rust.vim @@ -10,7 +10,7 @@ endif let b:did_indent = 1 setlocal cindent -setlocal cinoptions=L0,(0,Ws,JN +setlocal cinoptions=L0,(0,Ws,JN,j1 setlocal cinkeys=0{,0},!^F,o,O,0[,0] " Don't think cinwords will actually do anything at all... never mind setlocal cinwords=do,for,if,else,while,loop,impl,mod,unsafe,trait,struct,enum,fn,extern