diff --git a/src/lib/iter.rs b/src/lib/iter.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e99495515884ab3ebd1109c041db496a35de8b7d
--- /dev/null
+++ b/src/lib/iter.rs
@@ -0,0 +1,37 @@
+pub trait Batching<B, I, F>
+    where I: Iterator,
+          F: FnMut(&mut I) -> Option<B>,
+{
+    fn batching(self, func: F) -> BatchingIter<I, F>;
+}
+
+#[derive(Clone)]
+pub struct BatchingIter<I, F> {
+    iter: I,
+    func: F,
+}
+
+impl<B, I, F> Batching<B, I, F> for I
+    where I: Iterator,
+          F: FnMut(&mut I) -> Option<B>,
+{
+    fn batching(self, func: F) -> BatchingIter<I, F> {
+        BatchingIter { iter: self, func: func }
+    }
+}
+
+impl<B, I, F> Iterator for BatchingIter<I, F>
+    where I: Iterator,
+          F: FnMut(&mut I) -> Option<B>,
+{
+    type Item = B;
+    #[inline]
+    fn next(&mut self) -> Option<B> {
+        (self.func)(&mut self.iter)
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, None)
+    }
+}
diff --git a/src/lib/lib.rs b/src/lib/lib.rs
index f79b63a1d5990bf8a2f465f959c17b4b5ab243e5..26d087f01ae0f345fcf0e2877f28d470d8e8402a 100644
--- a/src/lib/lib.rs
+++ b/src/lib/lib.rs
@@ -3,6 +3,7 @@
 #![feature(integer_atomics)]
 #![feature(pointer_methods)]
 #![feature(getpid)]
+#![feature(nll)]
 
 #[macro_use]
 extern crate bitflags;
@@ -44,6 +45,7 @@ mod types;
 pub mod parser;
 mod ascii_helpers;
 mod builtins;
+mod iter;
 mod shell;
 
 pub use shell::{
diff --git a/src/lib/shell/binary/mod.rs b/src/lib/shell/binary/mod.rs
index c1c37875cd909fe8b6bfa5211c54a8893ac2afe5..35cecdcce22ae3f67796a165c76961552fb6d420 100644
--- a/src/lib/shell/binary/mod.rs
+++ b/src/lib/shell/binary/mod.rs
@@ -11,6 +11,7 @@ use self::{
 };
 use super::{flow_control::Statement, status::*, FlowLogic, Shell, ShellHistory};
 use liner::{Buffer, Context};
+use iter::Batching;
 use std::{env, fs::File, io::ErrorKind, iter, path::Path, process};
 
 pub const MAN_ION: &'static str = r#"NAME
@@ -186,41 +187,46 @@ impl Binary for Shell {
     fn prompt(&mut self) -> String { prompt(self) }
 }
 
-// TODO: Convert this into an iterator to eliminate heap allocations.
-fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> {
-    let mut res = Vec::new();
+fn word_divide<'a>(buf: &'a Buffer) -> Vec<(usize, usize)> {  // -> impl Iterator<Item = (usize, usize)> + 'a
+
     let mut word_start = None;
 
-    macro_rules! check_boundary {
-        ($c: expr, $index: expr, $escaped: expr) => {{
-            if let Some(start) = word_start {
-                if $c == ' ' && !$escaped {
-                    res.push((start, $index));
-                    word_start = None;
-                }
+    #[inline]
+    fn check_boundary(word_start: &mut Option<usize>, c: char, index: usize, escaped: bool) -> Option<Result<(usize, usize), ()>> {
+        if let &mut Some(start) = word_start {
+            if c == ' ' && !escaped {
+                *word_start = None;
+                Some(Ok((start, index)))
             } else {
-                if $c != ' ' {
-                    word_start = Some($index);
-                }
+                Some(Err(()))
+            }
+        } else {
+            if c != ' ' {
+                *word_start = Some(index);
             }
-        }};
+            Some(Err(()))
+        }
     }
 
-    let mut iter = buf.chars().enumerate();
-    while let Some((i, &c)) = iter.next() {
-        match c {
-            '\\' => {
+    buf.chars().enumerate().batching(|iter| {
+        match iter.next() {
+            Some((i, '\\')) => {
                 if let Some((_, &cnext)) = iter.next() {
                     // We use `i` in order to include the backslash as part of the word
-                    check_boundary!(cnext, i, true);
+                    check_boundary(&mut word_start, cnext, i, true)
+                } else {
+                    Some(Err(()))
                 }
             }
-            c => check_boundary!(c, i, false),
+            Some((i, c)) => check_boundary(&mut word_start, *c, i, false),
+            None => {
+                // When start has been set, that means we have encountered a full word.
+                word_start.map(|start| Ok((start, buf.num_chars())))
+            }
         }
-    }
-    if let Some(start) = word_start {
-        // When start has been set, that means we have encountered a full word.
-        res.push((start, buf.num_chars()));
-    }
-    res
+    })
+    .filter_map(|item| {
+        item.ok()
+    })
+    .collect()
 }