Repository: rinja-rs/askama (language: rust)

Issue:
Add support for reference operator (i.e. `{% let x = &y %}`)
Sometimes while working with deeply nested structures, it would be useful to create an alias to a field, like say `{% let h = &parent.child.hash %}`

At the moment the `&` unary operator is not supported.



Discussion / hints:
Not supporting this has been a design goal from the start. If I remember correctly, your example should already work without `&`.
> If I remember correctly, your example should already work without `&`.

Unfortunately it doesn't (`hash` here is an owned `HashMap`, and all other structures are owned by `self`):
~~~~
error[E0507]: cannot move out of `self.nested_1.nested_2.hash` which is behind a shared reference
   --> /home/build/sources/testing/tests/simple.rs:406:10
    |
406 | #[derive(askama::Template, Default)]
    |          ^^^^^^^^^^^^^^^^
    |          |
    |          move occurs because `self.nested_1.nested_2.hash` has type `std::collections::HashMap<&str, &str>`, which does not implement t
he `Copy` trait                                                                                                                                  |          help: consider borrowing here: `&askama::Template`
    |
    = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error
~~~~

Hmm, okay. Perhaps we should eagerly take a reference in variable declarations (and then not do so when referencing variables later).
I don't know this issue is still being tracked, by I solved by using a filter.

```
    pub fn asref<'a, T>(s: &'a T) -> askama::Result<&'a T> {
        Ok(s)
    }
```

And on the template:
```
{% let doc = invoice.0|asref %}
```
It is also possible to use https://doc.rust-lang.org/std/borrow/trait.Borrow.html

At least for my usecase where I needed to compare dates:
```html
{% if (Utc::now() + Duration::days(1)).borrow() > delete_at %}
    {{ delete_at.format("%H:%M:%S") }}
{% else %}
    {{ delete_at }}
{% endif %}

```

Fix patch:
diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index 0f9a2af0a..2040cac24 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -1210,6 +1210,20 @@ impl<'a> Generator<'a> {
         Ok(DisplayWrap::Wrapped)
     }
 
+    fn _visit_as_ref_filter(
+        &mut self,
+        buf: &mut Buffer,
+        args: &[Expr<'_>],
+    ) -> Result<(), CompileError> {
+        let arg = match args {
+            [arg] => arg,
+            _ => return Err("unexpected argument(s) in `as_ref` filter".into()),
+        };
+        buf.write("&");
+        self.visit_expr(buf, arg)?;
+        Ok(())
+    }
+
     fn visit_filter(
         &mut self,
         buf: &mut Buffer,
@@ -1230,6 +1244,9 @@ impl<'a> Generator<'a> {
             return Ok(DisplayWrap::Unwrapped);
         } else if name == "markdown" {
             return self._visit_markdown_filter(buf, args);
+        } else if name == "as_ref" {
+            self._visit_as_ref_filter(buf, args)?;
+            return Ok(DisplayWrap::Wrapped);
         }
 
         if name == "tojson" {
diff --git a/book/src/filters.md b/book/src/filters.md
index e85068f53..ea8198a0e 100644
--- a/book/src/filters.md
+++ b/book/src/filters.md
@@ -21,6 +21,7 @@ but are disabled by default. Enable them with Cargo features (see below for more
 
 * **[Built-in filters][#built-in-filters]:**  
   [`abs`][#abs],
+  [`as_ref`][#as_ref],
   [`capitalize`][#capitalize],
   [`center`][#center],
   [`escape|e`][#escape],
@@ -62,6 +63,23 @@ Output:
 2
 ```
 
+### as_ref
+[#as_ref]: #as_ref
+
+Creates a reference to the given argument.
+
+```
+{{ "a"|as_ref }}
+{{ self.x|as_ref }}
+```
+
+will become:
+
+```
+&a
+&self.x
+```
+
 ### capitalize
 [#capitalize]: #capitalize
 


Test patch:
diff --git a/testing/tests/filters.rs b/testing/tests/filters.rs
index 5a02909e4..4fe5c62fd 100644
--- a/testing/tests/filters.rs
+++ b/testing/tests/filters.rs
@@ -309,3 +309,17 @@ fn test_json_script() {
         r#"<script>var user = "\u003c/script\u003e\u003cbutton\u003eHacked!\u003c/button\u003e"</script>"#
     );
 }
+
+#[derive(askama::Template)]
+#[template(source = "{% let word = s|as_ref %}{{ word }}", ext = "html")]
+struct LetBorrow {
+    s: String,
+}
+
+#[test]
+fn test_let_borrow() {
+    let template = LetBorrow {
+        s: "hello".to_owned(),
+    };
+    assert_eq!(template.render().unwrap(), "hello")
+}