Repository: mitsuhiko/minijinja (language: rust)

Issue:
2.0: One-Shot vs Repeatable Iteration
This is a followup to a discussion in #457

Today MiniJinja supports iterators that are one-shot iterators. If you iterate over them once, you are done. This is a pretty unfortunate user experience and with the current API in 2.0 for more of those cases a better iterator can be found: one that resets itself:

* `Value::from_iterator`: this takes a regular iterator and wraps it. It does not stringify and can only be iterated once.
* `Value::from_object_iter`: this takes an object and a function that returns an iterator. It stringifies like a sequence (for ease of debugging) and can be iterated more than once.

Neither of those can be indexed, both can return a `size_hint` so that the loop's special variables work.

The motivation for `Value::from_iterator` originally was not to expose iterable things to the engine, for that `Value::from_iter` was supposed to be used which creates a sequence, but to allow (near-)endless streaming of templates. There are some potential alternatives here.

1. Keep the current interface.
2. endless streaming should be started by a function call. Thus rather than passing the iterator, you pass an `FnOnce` that starts the stream on iteration.
3. endless streaming should be started by a function call. Rather than `FnOnce` it's an `Fn` and users are encouraged to make it error manually if started more than once or allow the engine to restart the stream upon each iteration.
4. endless streaming is a special case that only `{% for %}` knows about.

There might be more options.


Discussion / hints:

Fix patch:
diff --git a/examples/dynamic-objects/src/main.rs b/examples/dynamic-objects/src/main.rs
index a8c1a885..f70abb88 100644
--- a/examples/dynamic-objects/src/main.rs
+++ b/examples/dynamic-objects/src/main.rs
@@ -80,7 +80,7 @@ fn main() {
     env.add_function("cycler", make_cycler);
     env.add_global("magic", Value::from_object(Magic));
     env.add_global("seq", Value::from_object(SimpleDynamicSeq));
-    env.add_global("real_iter", Value::from_iterator((0..10).chain(20..30)));
+    env.add_global("real_iter", Value::make_iterable(|| (0..10).chain(20..30)));
     env.add_template("template.html", include_str!("template.html"))
         .unwrap();
 
diff --git a/minijinja/src/filters.rs b/minijinja/src/filters.rs
index 6923af57..75e0a42e 100644
--- a/minijinja/src/filters.rs
+++ b/minijinja/src/filters.rs
@@ -294,7 +294,7 @@ mod builtins {
 
     use crate::error::ErrorKind;
     use crate::value::ops::as_f64;
-    use crate::value::{Kwargs, ObjectRepr, ValueKind, ValueRepr};
+    use crate::value::{Kwargs, ValueKind, ValueRepr};
     use std::borrow::Cow;
     use std::cmp::Ordering;
     use std::fmt::Write;
@@ -494,7 +494,7 @@ mod builtins {
         }
     }
 
-    /// Reverses a list or string
+    /// Reverses an iterable or string
     ///
     /// ```jinja
     /// {% for user in users|reverse %}
@@ -503,19 +503,7 @@ mod builtins {
     /// ```
     #[cfg_attr(docsrs, doc(cfg(feature = "builtins")))]
     pub fn reverse(v: Value) -> Result<Value, Error> {
-        if let Some(s) = v.as_str() {
-            Ok(Value::from(s.chars().rev().collect::<String>()))
-        } else if let Some(iter) = v.as_object().and_then(|x| x.try_iter()) {
-            // TODO: support reversible iterators?
-            let mut values = iter.collect::<Vec<_>>();
-            values.reverse();
-            Ok(Value::from_iter(values))
-        } else {
-            Err(Error::new(
-                ErrorKind::InvalidOperation,
-                format!("cannot reverse value of type {}", v.kind()),
-            ))
-        }
+        v.reverse()
     }
 
     /// Trims a value
@@ -741,7 +729,7 @@ mod builtins {
         }
     }
 
-    /// Returns the last item from a list.
+    /// Returns the last item from an iterable or string.
     ///
     /// If the list is empty `undefined` is returned.
     ///
@@ -760,15 +748,10 @@ mod builtins {
     pub fn last(value: Value) -> Result<Value, Error> {
         if let Some(s) = value.as_str() {
             Ok(s.chars().next_back().map_or(Value::UNDEFINED, Value::from))
-        } else if let Some(obj) = value
-            .as_object()
-            .filter(|x| matches!(x.repr(), ObjectRepr::Seq))
-        {
-            Ok(match obj.len() {
-                Some(0) | None => None,
-                Some(idx) => obj.get_value(&Value::from(idx - 1)),
-            }
-            .unwrap_or_default())
+        } else if matches!(value.kind(), ValueKind::Seq | ValueKind::Iterable) {
+            let rev = ok!(value.reverse());
+            let mut iter = ok!(rev.try_iter());
+            Ok(iter.next().unwrap_or_default())
         } else {
             Err(Error::new(
                 ErrorKind::InvalidOperation,
diff --git a/minijinja/src/value/argtypes.rs b/minijinja/src/value/argtypes.rs
index 01c077fe..9faf354f 100644
--- a/minijinja/src/value/argtypes.rs
+++ b/minijinja/src/value/argtypes.rs
@@ -299,23 +299,19 @@ impl From<()> for Value {
     }
 }
 
-// FIXME: Make this efficient.
 impl<V: Into<Value>> FromIterator<V> for Value {
     fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
-        let vec = iter.into_iter().map(Into::into).collect::<Vec<Value>>();
-        Value::from_object(vec)
+        Value::from_object(iter.into_iter().map(Into::into).collect::<Vec<Value>>())
     }
 }
 
-// FIXME: Make this efficient.
 impl<K: Into<Value>, V: Into<Value>> FromIterator<(K, V)> for Value {
     fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
-        let map = iter
-            .into_iter()
-            .map(|(k, v)| (k.into(), v.into()))
-            .collect::<ValueMap>();
-
-        Value::from_object(map)
+        Value::from_object(
+            iter.into_iter()
+                .map(|(k, v)| (k.into(), v.into()))
+                .collect::<ValueMap>(),
+        )
     }
 }
 
@@ -972,10 +968,9 @@ impl<'a, T: ArgType<'a, Output = T>> ArgType<'a> for Vec<T> {
             Some(value) => {
                 let iter = ok!(value
                     .as_object()
-                    // XXX: needs updating for iterators
-                    .filter(|x| matches!(x.repr(), ObjectRepr::Seq))
+                    .filter(|x| matches!(x.repr(), ObjectRepr::Seq | ObjectRepr::Iterable))
                     .and_then(|x| x.try_iter())
-                    .ok_or_else(|| { Error::new(ErrorKind::InvalidOperation, "not a sequence") }));
+                    .ok_or_else(|| { Error::new(ErrorKind::InvalidOperation, "not iterable") }));
                 let mut rv = Vec::new();
                 for value in iter {
                     rv.push(ok!(T::from_value_owned(value)));
@@ -988,10 +983,9 @@ impl<'a, T: ArgType<'a, Output = T>> ArgType<'a> for Vec<T> {
     fn from_value_owned(value: Value) -> Result<Self, Error> {
         let iter = ok!(value
             .as_object()
-            // XXX: needs updating for iterators
-            .filter(|x| matches!(x.repr(), ObjectRepr::Seq))
+            .filter(|x| matches!(x.repr(), ObjectRepr::Seq | ObjectRepr::Iterable))
             .and_then(|x| x.try_iter())
-            .ok_or_else(|| { Error::new(ErrorKind::InvalidOperation, "not a sequence") }));
+            .ok_or_else(|| { Error::new(ErrorKind::InvalidOperation, "not iterable") }));
         let mut rv = Vec::new();
         for value in iter {
             rv.push(ok!(T::from_value_owned(value)));
diff --git a/minijinja/src/value/mod.rs b/minijinja/src/value/mod.rs
index 72850790..9d7a84e4 100644
--- a/minijinja/src/value/mod.rs
+++ b/minijinja/src/value/mod.rs
@@ -20,11 +20,10 @@
 //! let true_value = Value::from(true);
 //! ```
 //!
-//! Or via the [`FromIterator`] trait:
+//! Or via the [`FromIterator`] trait which can create sequences or maps:
 //!
 //! ```
 //! # use minijinja::value::Value;
-//!
 //! // collection into a sequence
 //! let value: Value = (1..10).into_iter().collect();
 //!
@@ -34,11 +33,12 @@
 //!
 //! For certain types of iterators (`Send` + `Sync` + `'static`) it's also
 //! possible to make the value lazily iterate over the value by using the
-//! `Value::from_iterator` function instead:
+//! `Value::make_iterable` function instead.  Whenever the value requires
+//! iteration, the function is called to create that iterator.
 //!
 //! ```
 //! # use minijinja::value::Value;
-//! let value: Value = Value::from_iterator(1..10);
+//! let value: Value = Value::make_iterable(|| 1..10);
 //! ```
 //!
 //! To to into the inverse directly the various [`TryFrom`](std::convert::TryFrom)
@@ -106,7 +106,7 @@ let vec = Vec::<i32>::deserialize(value).unwrap();
 //! Values can also hold "dynamic" objects.  These are objects which implement the
 //! [`Object`] trait.  These can be used to implement dynamic functionality such
 //! as stateful values and more.  Dynamic objects are internally also used to
-//! implement the special `loop` variable or macros.
+//! implement the special `loop` variable, macros and similar things.
 //!
 //! To create a dynamic `Value` object or [`Value::from_object`],
 //! [`Value::from_dyn_object`]:
@@ -267,6 +267,8 @@ pub enum ValueKind {
     Iterable,
     /// A plain object without specific behavior.
     Plain,
+    /// This value is invalid.  This can happen when a serialization error occurred.
+    Invalid,
 }
 
 impl fmt::Display for ValueKind {
@@ -282,6 +284,7 @@ impl fmt::Display for ValueKind {
             ValueKind::Map => "map",
             ValueKind::Iterable => "iterator",
             ValueKind::Plain => "plain object",
+            ValueKind::Invalid => "invalid value",
         })
     }
 }
@@ -599,57 +602,57 @@ impl Value {
         Value::from(ValueRepr::Object(value.into()))
     }
 
-    /// Creates a value from an iterator.
+    /// Creates a value that is an iterable.
     ///
-    /// This takes an iterator (yielding values that can be turned into a [`Value`])
-    /// and returns a value that can be iterated over exactly once.
+    /// The function is invoked to create a new iterator every time the value is
+    /// iterated over.
+    ///
+    /// ```
+    /// # use minijinja::value::Value;
+    /// let val = Value::make_iterable(|| 0..10);
+    /// ```
     ///
     /// Iterators that implement [`ExactSizeIterator`] or have a matching lower and upper
     /// bound on the [`Iterator::size_hint`] report a known `loop.length`.  Iterators that
     /// do not fulfill these requirements will not.  The same is true for `revindex` and
     /// similar properties.
-    ///
-    /// ```
-    /// # use minijinja::value::Value;
-    /// let val = Value::from_iterator(0..10);
-    /// ```
-    pub fn from_iterator<I, T>(iter: I) -> Value
+    pub fn make_iterable<I, T, F>(maker: F) -> Value
     where
         I: Iterator<Item = T> + Send + Sync + 'static,
-        T: Into<Value> + 'static,
+        T: Into<Value> + Send + Sync + 'static,
+        F: Fn() -> I + Send + Sync + 'static,
     {
-        struct SimpleIteratorObject<I, T>(Mutex<Option<I>>)
+        struct Iterable<I, T, F>(F)
         where
             I: Iterator<Item = T> + Send + Sync + 'static,
-            T: Into<Value> + 'static;
+            T: Into<Value> + Send + Sync + 'static,
+            F: Fn() -> I + Send + Sync;
 
-        impl<I, T> fmt::Debug for SimpleIteratorObject<I, T>
+        impl<I, T, F> fmt::Debug for Iterable<I, T, F>
         where
             I: Iterator<Item = T> + Send + Sync + 'static,
-            T: Into<Value> + 'static,
+            T: Into<Value> + Send + Sync + 'static,
+            F: Fn() -> I + Send + Sync,
         {
             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                 f.debug_tuple("<iterator>").finish()
             }
         }
 
-        impl<I, T> Object for SimpleIteratorObject<I, T>
+        impl<I, T, F> Object for Iterable<I, T, F>
         where
             I: Iterator<Item = T> + Send + Sync + 'static,
-            T: Into<Value> + 'static,
+            T: Into<Value> + Send + Sync + 'static,
+            F: Fn() -> I + Send + Sync,
         {
             fn repr(self: &Arc<Self>) -> ObjectRepr {
                 ObjectRepr::Iterable
             }
 
-            fn len(self: &Arc<Self>) -> Option<usize> {
-                None
-            }
-
             fn enumerate(self: &Arc<Self>) -> Enumerator {
-                let iter = self.0.lock().unwrap().take();
+                let iter = (self.0)();
 
-                struct Iter<I, T>(Option<I>)
+                struct Iter<I, T>(I)
                 where
                     I: Iterator<Item = T> + Send + Sync + 'static,
                     T: Into<Value> + 'static;
@@ -662,40 +665,34 @@ impl Value {
                     type Item = Value;
 
                     fn next(&mut self) -> Option<Self::Item> {
-                        match &mut self.0 {
-                            Some(x) => x.next().map(Into::into),
-                            None => None,
-                        }
+                        self.0.next().map(Into::into)
                     }
 
                     fn size_hint(&self) -> (usize, Option<usize>) {
-                        match &self.0 {
-                            Some(x) => x.size_hint(),
-                            None => (0, Some(0)),
-                        }
+                        self.0.size_hint()
                     }
                 }
 
                 Enumerator::Iter(Box::new(Iter(iter)))
             }
         }
-        Value::from_object(SimpleIteratorObject(Mutex::new(Some(iter.fuse()))))
+        Value::from_object(Iterable(maker))
     }
 
-    /// Creates an iterator that iterates over the given value.
+    /// Creates an iterable that iterates over the given value.
     ///
-    /// Unlike [`from_iterator`](Self::from_iterator) this returns an iterator
-    /// that can be iterated over multiple times.  Because of this it also will
-    /// render like a sequence when debug printed.
+    /// This is similar to [`make_iterable`](Self::make_iterable) but it takes an extra
+    /// reference to a value it can borrow out from.  It's a bit less generic in that it
+    /// needs to return a boxed iterator of values directly.
     ///
     /// ```rust
     /// # use minijinja::value::Value;
-    /// let val = Value::from_object_iter(vec![1, 2, 3], |vec| {
+    /// let val = Value::make_object_iterable(vec![1, 2, 3], |vec| {
     ///     Box::new(vec.iter().copied().map(Value::from))
     /// });
     /// assert_eq!(val.to_string(), "[1, 2, 3]");
     /// ````
-    pub fn from_object_iter<T, F>(object: T, maker: F) -> Value
+    pub fn make_object_iterable<T, F>(object: T, maker: F) -> Value
     where
         T: Send + Sync + 'static,
         F: for<'a> Fn(&'a T) -> Box<dyn Iterator<Item = Value> + Send + Sync + 'a>
@@ -703,18 +700,18 @@ impl Value {
             + Sync
             + 'static,
     {
-        struct FromObjectIter<T, F> {
+        struct Iterable<T, F> {
             maker: F,
             object: T,
         }
 
-        impl<T, F> fmt::Debug for FromObjectIter<T, F> {
+        impl<T, F> fmt::Debug for Iterable<T, F> {
             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                f.debug_struct("FromObjectIter").finish()
+                f.debug_struct("<iterator>").finish()
             }
         }
 
-        impl<T, F> Object for FromObjectIter<T, F>
+        impl<T, F> Object for Iterable<T, F>
         where
             T: Send + Sync + 'static,
             F: for<'a> Fn(&'a T) -> Box<dyn Iterator<Item = Value> + Send + Sync + 'a>
@@ -727,12 +724,12 @@ impl Value {
             }
 
             fn enumerate(self: &Arc<Self>) -> Enumerator {
-                struct IterWrapper {
+                struct Iter {
                     iter: Box<dyn Iterator<Item = Value> + Send + Sync + 'static>,
                     _object: DynObject,
                 }
 
-                impl Iterator for IterWrapper {
+                impl Iterator for Iter {
                     type Item = Value;
 
                     fn next(&mut self) -> Option<Self::Item> {
@@ -744,15 +741,45 @@ impl Value {
                     }
                 }
 
-                let iter: Box<dyn Iterator<Item = Value> + Send + Sync + '_> =
-                    (self.maker)(&self.object);
-                let iter = unsafe { std::mem::transmute(iter) };
+                // SAFETY: this is safe because the object is kept alive by the iter
+                let iter = unsafe { std::mem::transmute((self.maker)(&self.object)) };
                 let _object = DynObject::new(self.clone());
-                Enumerator::Iter(Box::new(IterWrapper { iter, _object }))
+                Enumerator::Iter(Box::new(Iter { iter, _object }))
             }
         }
 
-        Value::from_object(FromObjectIter { maker, object })
+        Value::from_object(Iterable { maker, object })
+    }
+
+    /// Creates a value from a one-shot iterator.
+    ///
+    /// This takes an iterator (yielding values that can be turned into a [`Value`])
+    /// and wraps it in a way that it turns into an iterable value.  From the view of
+    /// the template this can be iterated over exactly once for the most part once
+    /// exhausted.
+    ///
+    /// Such iterators are strongly recommended against in the general sense due to
+    /// their surprising behavior, but they can be useful for more advanced use
+    /// cases where data should be streamed into the template as it becomes available.
+    ///
+    /// Such iterators never have any size hints.
+    ///
+    /// ```
+    /// # use minijinja::value::Value;
+    /// let val = Value::make_one_shot_iterator(0..10);
+    /// ```
+    ///
+    /// Attempting to iterate over it a second time will not yield any more items.
+    pub fn make_one_shot_iterator<I, T>(iter: I) -> Value
+    where
+        I: Iterator<Item = T> + Send + Sync + 'static,
+        T: Into<Value> + Send + Sync + 'static,
+    {
+        let iter = Arc::new(Mutex::new(iter.fuse()));
+        Value::make_iterable(move || {
+            let iter = iter.clone();
+            std::iter::from_fn(move || iter.lock().unwrap().next())
+        })
     }
 
     /// Creates a callable value from a function.
@@ -786,8 +813,7 @@ impl Value {
             ValueRepr::String(..) => ValueKind::String,
             ValueRepr::Bytes(_) => ValueKind::Bytes,
             ValueRepr::U128(_) => ValueKind::Number,
-            // XXX: invalid values report themselves as maps which is a lie
-            ValueRepr::Invalid(_) => ValueKind::Undefined,
+            ValueRepr::Invalid(_) => ValueKind::Invalid,
             ValueRepr::Object(ref obj) => match obj.repr() {
                 ObjectRepr::Map => ValueKind::Map,
                 ObjectRepr::Seq => ValueKind::Seq,
@@ -1008,30 +1034,87 @@ impl Value {
     /// # Ok(()) }
     /// ```
     pub fn try_iter(&self) -> Result<ValueIter, Error> {
-        let imp = match self.0 {
-            ValueRepr::None | ValueRepr::Undefined => ValueIterImpl::Empty,
+        match self.0 {
+            ValueRepr::None | ValueRepr::Undefined => Some(ValueIterImpl::Empty),
             ValueRepr::String(ref s, _) => {
-                ValueIterImpl::Chars(0, s.chars().count(), Arc::clone(s))
+                Some(ValueIterImpl::Chars(0, s.chars().count(), Arc::clone(s)))
             }
-            ValueRepr::Object(ref obj) => {
-                if let Some(iter) = obj.try_iter() {
-                    ValueIterImpl::Dyn(iter)
-                } else {
-                    return Err(Error::new(
-                        ErrorKind::InvalidOperation,
-                        format!("{} is not iterable", self.kind()),
-                    ));
-                }
-            }
-            _ => {
-                return Err(Error::new(
-                    ErrorKind::InvalidOperation,
-                    format!("{} is not iterable", self.kind()),
-                ))
-            }
-        };
+            ValueRepr::Object(ref obj) => obj.try_iter().map(ValueIterImpl::Dyn),
+            _ => None,
+        }
+        .map(|imp| ValueIter { imp })
+        .ok_or_else(|| {
+            Error::new(
+                ErrorKind::InvalidOperation,
+                format!("{} is not iterable", self.kind()),
+            )
+        })
+    }
 
-        Ok(ValueIter { imp })
+    /// Returns a reversed view of this value.
+    ///
+    /// This is implemented for the following types with the following behaviors:
+    ///
+    /// * undefined or none: value returned unchanged.
+    /// * string and bytes: returns a reversed version of that value
+    /// * iterables: returns a reversed version of the iterable.  If the iterable is not
+    ///   reversable itself, it consumes it and then reverses it.
+    pub fn reverse(&self) -> Result<Value, Error> {
+        match self.0 {
+            ValueRepr::Undefined | ValueRepr::None => Some(self.clone()),
+            ValueRepr::String(ref s, _) => Some(Value::from(s.chars().rev().collect::<String>())),
+            ValueRepr::Bytes(ref b) => {
+                Some(Value::from(b.iter().rev().copied().collect::<Vec<_>>()))
+            }
+            ValueRepr::Object(ref o) => match o.enumerate() {
+                Enumerator::NonEnumerable => None,
+                Enumerator::Empty => Some(Value::make_iterable(|| None::<Value>.into_iter())),
+                Enumerator::Seq(l) => {
+                    let self_clone = o.clone();
+                    Some(Value::make_iterable(move || {
+                        let self_clone = self_clone.clone();
+                        (0..l).rev().map(move |idx| {
+                            self_clone.get_value(&Value::from(idx)).unwrap_or_default()
+                        })
+                    }))
+                }
+                Enumerator::Iter(iter) => {
+                    let mut v = iter.collect::<Vec<_>>();
+                    v.reverse();
+                    Some(Value::make_object_iterable(v, move |v| {
+                        Box::new(v.iter().cloned())
+                    }))
+                }
+                Enumerator::RevIter(rev_iter) => {
+                    let for_restart = self.clone();
+                    let iter = Mutex::new(Some(rev_iter));
+                    Some(Value::make_iterable(move || {
+                        if let Some(iter) = iter.lock().unwrap().take() {
+                            Box::new(iter) as Box<dyn Iterator<Item = Value> + Send + Sync>
+                        } else if let Ok(iter) = for_restart.try_iter() {
+                            Box::new(iter) as Box<dyn Iterator<Item = Value> + Send + Sync>
+                        } else {
+                            Box::new(None.into_iter())
+                                as Box<dyn Iterator<Item = Value> + Send + Sync>
+                        }
+                    }))
+                }
+                Enumerator::Str(s) => Some(Value::make_iterable(move || s.iter().rev().copied())),
+                Enumerator::Values(mut v) => {
+                    v.reverse();
+                    Some(Value::make_object_iterable(v, move |v| {
+                        Box::new(v.iter().cloned())
+                    }))
+                }
+            },
+            _ => None,
+        }
+        .ok_or_else(|| {
+            Error::new(
+                ErrorKind::InvalidOperation,
+                format!("cannot reverse values of type {}", self.kind()),
+            )
+        })
     }
 
     /// Returns some reference to the boxed object if it is of type `T`, or None if it isn’t.
diff --git a/minijinja/src/value/object.rs b/minijinja/src/value/object.rs
index 22acc815..e045ab91 100644
--- a/minijinja/src/value/object.rs
+++ b/minijinja/src/value/object.rs
@@ -10,6 +10,9 @@ use crate::vm::State;
 
 /// A trait that represents a dynamic object.
 ///
+/// There is a type erased wrapper of this trait available called
+/// [`DynObject`] which is what the engine actually holds internally.
+///
 /// # Basic Struct
 ///
 /// The following example shows how to implement a dynamic object which
@@ -81,6 +84,33 @@ use crate::vm::State;
 /// let value = Value::from_object(Point(1.0, 2.5, 3.0));
 /// ```
 ///
+/// # Iterables
+///
+/// If you have something that is not quite a sequence but is capable of yielding
+/// values over time, you can directly implement an iterable.  This is somewhat
+/// uncommon as you can normally directly use [`Value::make_iterable`].  Here
+/// is how this can be done though:
+///
+/// ```
+/// use std::sync::Arc;
+/// use minijinja::value::{Value, Object, ObjectRepr, Enumerator};
+///
+/// #[derive(Debug)]
+/// struct Range10;
+///
+/// impl Object for Range10 {
+///     fn repr(self: &Arc<Self>) -> ObjectRepr {
+///         ObjectRepr::Iterable
+///     }
+///
+///     fn enumerate(self: &Arc<Self>) -> Enumerator {
+///         Enumerator::Iter(Box::new((1..10).map(Value::from)))
+///     }
+/// }
+///
+/// let value = Value::from_object(Range10);
+/// ```
+///
 /// # Map As Context
 ///
 /// Map can also be used as template rendering context.  This has a lot of
@@ -154,7 +184,7 @@ pub trait Object: fmt::Debug + Send + Sync {
 
     /// Returns the length of the object.
     ///
-    /// By default the length is taken from the `Enumerator`.  This means that in order
+    /// By default the length is taken from [`Enumerator::len`].  This means that in order
     /// to determine the length, an iteration is started.  If you this is a problem for
     /// your uses, you can manually implement this.  This might for instance be needed
     /// if your type can only be iterated over once.
@@ -270,6 +300,7 @@ macro_rules! impl_object_helpers {
                     })))
                 }
                 Enumerator::Iter(iter) => Some(iter),
+                Enumerator::RevIter(iter) => Some(Box::new(iter)),
                 Enumerator::Str(s) => Some(Box::new(s.iter().copied().map(intern).map(Value::from))),
                 Enumerator::Values(v) => Some(Box::new(v.into_iter())),
             }
@@ -323,14 +354,53 @@ pub trait ObjectExt: Object + Send + Sync + 'static {
             }
         }
 
-        let iter: Box<dyn Iterator<Item = Value> + Send + Sync + '_> = maker(self);
-
         // SAFETY: this is safe because the `IterObject` will keep our object alive.
-        let iter = unsafe { std::mem::transmute(iter) };
+        let iter = unsafe { std::mem::transmute(maker(self)) };
         let _object = self.clone();
         Enumerator::Iter(Box::new(IterObject { iter, _object }))
     }
 
+    /// Creates a new enumeration that projects into the given object supporting reversing.
+    fn mapped_rev_enumerator<F>(self: &Arc<Self>, maker: F) -> Enumerator
+    where
+        F: for<'a> FnOnce(
+                &'a Self,
+            )
+                -> Box<dyn DoubleEndedIterator<Item = Value> + Send + Sync + 'a>
+            + Send
+            + Sync
+            + 'static,
+        Self: Sized,
+    {
+        struct IterObject<T> {
+            iter: Box<dyn DoubleEndedIterator<Item = Value> + Send + Sync + 'static>,
+            _object: Arc<T>,
+        }
+
+        impl<T> Iterator for IterObject<T> {
+            type Item = Value;
+
+            fn next(&mut self) -> Option<Self::Item> {
+                self.iter.next()
+            }
+
+            fn size_hint(&self) -> (usize, Option<usize>) {
+                self.iter.size_hint()
+            }
+        }
+
+        impl<T> DoubleEndedIterator for IterObject<T> {
+            fn next_back(&mut self) -> Option<Self::Item> {
+                self.iter.next_back()
+            }
+        }
+
+        // SAFETY: this is safe because the `IterObject` will keep our object alive.
+        let iter = unsafe { std::mem::transmute(maker(self)) };
+        let _object = self.clone();
+        Enumerator::RevIter(Box::new(IterObject { iter, _object }))
+    }
+
     impl_object_helpers!(&Arc<Self>);
 }
 
@@ -351,8 +421,10 @@ pub enum Enumerator {
     ///
     /// This has a known length which is the length of the slice.
     Str(&'static [&'static str]),
-    /// A dynamic iterator over keys.  Length is known if the size hint has matching lower and upper bounds.
+    /// A dynamic iterator over values.  Length is known if the size hint has matching lower and upper bounds.
     Iter(Box<dyn Iterator<Item = Value> + Send + Sync>),
+    /// Like `Iter` but supports efficient reversing.
+    RevIter(Box<dyn DoubleEndedIterator<Item = Value> + Send + Sync>),
     /// Instructs the engine to yield values by calling `get_value` from 0 to `usize`.
     ///
     /// This has a known legth of `usize`.
@@ -448,6 +520,10 @@ impl Enumerator {
                 (a, Some(b)) if a == b => a,
                 _ => return None,
             },
+            Enumerator::RevIter(i) => match i.size_hint() {
+                (a, Some(b)) if a == b => a,
+                _ => return None,
+            },
             Enumerator::Seq(v) => *v,
             Enumerator::NonEnumerable => return None,
         })
@@ -479,7 +555,7 @@ impl Object for ValueMap {
     }
 
     fn enumerate(self: &Arc<Self>) -> Enumerator {
-        self.mapped_enumerator(|this| Box::new(this.keys().cloned()))
+        self.mapped_rev_enumerator(|this| Box::new(this.keys().cloned()))
     }
 }
 
diff --git a/minijinja/src/value/ops.rs b/minijinja/src/value/ops.rs
index 63496ebf..1eefb6d5 100644
--- a/minijinja/src/value/ops.rs
+++ b/minijinja/src/value/ops.rs
@@ -113,17 +113,17 @@ pub fn slice(value: Value, start: Value, stop: Value, step: Value) -> Result<Val
             ))
         }
         ValueRepr::Undefined | ValueRepr::None => Ok(Value::from(Vec::<Value>::new())),
-        ValueRepr::Object(obj) if obj.repr() == ObjectRepr::Seq => {
-            let len = obj.len().unwrap_or_default();
-            let (start, len) = get_offset_and_len(start, stop, || len);
-            Ok(Value::from_iter(
-                obj.try_iter()
-                    .into_iter()
-                    .flatten()
-                    .skip(start)
-                    .take(len)
-                    .step_by(step),
-            ))
+        ValueRepr::Object(obj) if matches!(obj.repr(), ObjectRepr::Seq | ObjectRepr::Iterable) => {
+            Ok(Value::make_object_iterable(obj, move |obj| {
+                let len = obj.len().unwrap_or_default();
+                let (start, len) = get_offset_and_len(start, stop, || len);
+                // The manual match here is important that we do not mess up the size_hint
+                if let Some(iter) = obj.try_iter() {
+                    Box::new(iter.skip(start).take(len).step_by(step))
+                } else {
+                    Box::new(None.into_iter())
+                }
+            }))
         }
         _ => error,
     }


Test patch:
diff --git a/minijinja/tests/test_value.rs b/minijinja/tests/test_value.rs
index b01cccb9..32b83c0a 100644
--- a/minijinja/tests/test_value.rs
+++ b/minijinja/tests/test_value.rs
@@ -477,8 +477,8 @@ fn test_seq_object_borrow() {
 }
 
 #[test]
-fn test_iterator() {
-    let value = Value::from_iterator(0..10);
+fn test_one_shot_iterator() {
+    let value = Value::make_one_shot_iterator(0..10);
     assert_eq!(value.to_string(), "<iterator>");
     let rv = render!(
         "{% for item in iter %}[{{ item }}]{% endfor %}",
@@ -486,9 +486,38 @@ fn test_iterator() {
     );
     assert_snapshot!(rv, @"[0][1][2][3][4][5][6][7][8][9]");
 
+    let rv = render!(
+        "{% for item in iter %}- {{ item }}: {{ loop.index }} / {{ loop.length|default('?') }}\n{% endfor %}",
+        iter => Value::make_one_shot_iterator('a'..'f')
+    );
+    assert_snapshot!(rv, @r###"
+    - a: 1 / ?
+    - b: 2 / ?
+    - c: 3 / ?
+    - d: 4 / ?
+    - e: 5 / ?
+    "###);
+
+    let rv = render!(
+        "{% for item in iter %}{{ item }}{% endfor %}{% for item in iter %}{{ item }}{% endfor %}",
+        iter => Value::make_one_shot_iterator('a'..'f')
+    );
+    assert_snapshot!(rv, @r###"abcde"###);
+}
+
+#[test]
+fn test_make_iterable() {
+    let value = Value::make_iterable(|| 0..10);
+    assert_eq!(value.to_string(), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
+    let rv = render!(
+        "{% for item in iter %}[{{ item }}]{% endfor %}",
+        iter => value
+    );
+    assert_snapshot!(rv, @"[0][1][2][3][4][5][6][7][8][9]");
+
     let rv = render!(
         "{% for item in iter %}- {{ item }}: {{ loop.index }} / {{ loop.length }}\n{% endfor %}",
-        iter => Value::from_iterator('a'..'f')
+        iter => Value::make_iterable(|| 'a'..'f')
     );
     assert_snapshot!(rv, @r###"
     - a: 1 / 5
@@ -500,7 +529,7 @@ fn test_iterator() {
 
     let rv = render!(
         "{% for item in iter %}- {{ item }}: {{ loop.index }} / {{ loop.length|default('?') }}\n{% endfor %}",
-     iter => Value::from_iterator((0..10).filter(|x| x % 2 == 0))
+     iter => Value::make_iterable(|| (0..10).filter(|x| x % 2 == 0))
     );
     assert_snapshot!(rv, @r###"
     - 0: 1 / ?