# Injection-proof interpolation without variadics

**URL:** <https://internals.rust-lang.org/t/injection-proof-interpolation-without-variadics/17436>\
**Category:** language design\
**Created:** [September 22, 2022, 9:34pm UTC](https://internals.rust-lang.org/t/injection-proof-interpolation-without-variadics/17436 "2022-09-22T21:34:48Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![matklad](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matklad/32/12266_2.png) [@matklad](https://internals.rust-lang.org/u/matklad)\
**Post date:** [September 22, 2022, 9:34pm UTC](https://internals.rust-lang.org/t/injection-proof-interpolation-without-variadics/17436/1 "2022-09-22T21:34:48Z")

</div>

Continuing the discussion from [How to allow arbitrary expressions in format strings](https://internals.rust-lang.org/t/how-to-allow-arbitrary-expressions-in-format-strings/15812/28):

So, one of my long-standing desires for rust is to allow generic a interpolation syntax, like `"hello {name}"` which doesn't just convert stuff to string using hard-coded Display, but allows the library to interpose custom escaping logic. Basically, I want API like JS or Java template literals, which allow user code to get separate arrays of string fragments and interpolated values. This sounds terribly confusing if you are not already familiar to the idea, but luckily [JEP 430: String Templates (Preview)](https://openjdk.org/jeps/430) explains this much clearer than I could after staring at the screen for 12 hours 🙂

I long believed that that isn't actually possible to express without either variadic generics or dynamic casting, but, after reading that jep, I've realized today that such a generic interpolation API is actually possible in today's rust!

Basically, a macro can expand `"{x} + {y}"` into `Unnamable(("", " + ", ""), (x, y))` where `Unnamable` implements visitor pattern like this one:

```rust
pub trait TemplateString<V: TemplateVisitor> {
    fn accept(self) -> V::Output;
}

pub trait TemplateVisitor {
    type Output;
    fn new() -> Self;
    fn visit_str(&mut self, s: &'static str);
    fn finish(self) -> Self::Output;
}

pub trait TemplateVisit<T> {
    fn visit(&mut self, value: &T);
}

```

So, something like

```rust
#[test]
fn shell_escape() {
    let branch = "lol; echo pwned!";
    let s = interpol!("git switch {branch}" as ShellEscape);
    assert_eq!(s, r#"git switch "lol; echo pwned!""#);
}

```

gets expanded to

```rust
    struct TS<'a, T0>((&'static str, &'static str), (&'a T0,));

    impl<'a, T0, V: my_desire::TemplateVisitor + my_desire::TemplateVisit<T0>>
        my_desire::TemplateString<V> for TS<'a, T0>
    {
        fn accept(self) -> V::Output {
            let mut v = V::new();
            v.visit_str(self.0 .0);
            v.visit(self.1 .0);
            v.visit_str(self.0 .1);
            v.finish()
        }
    }
    let ts = TS(("git switch ", ""), (&branch,));
    my_desire::TemplateString::<ShellEscape>::accept(ts)
}

```

I've published a quick proof of concept at [GitHub - matklad/my-desire](https://github.com/matklad/my-desire) / [https://crates.io/crates/my-desire](https://crates.io/crates/my-desire) I don't intend to push this to production readiness, so feel free to run with the idea!

More examples here:

> <https://github.com/matklad/my-desire/blob/master/tests/it.rs>

---

<div class="post-metadata">

**Author:** ![jhpratt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jhpratt/32/11640_2.png) [@jhpratt](https://internals.rust-lang.org/u/jhpratt)\
**Post date:** [September 22, 2022, 10:10pm UTC](https://internals.rust-lang.org/t/injection-proof-interpolation-without-variadics/17436/2 "2022-09-22T22:10:43Z")

</div>

I'm not super familiar with the internals of `format_args!`, but isn't the expansion into the string literal and interpolated values already what's done? The only issue to my knowledge was the various modifiers supported by `format_args!` (which this wholly avoids).

---

<div class="post-metadata">

**Author:** ![matklad](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matklad/32/12266_2.png) [@matklad](https://internals.rust-lang.org/u/matklad)\
**Post date:** [September 22, 2022, 10:17pm UTC](https://internals.rust-lang.org/t/injection-proof-interpolation-without-variadics/17436/3 "2022-09-22T22:17:30Z")

</div>

format\_args doesn’t support custom escaping logic. I can’t use format-args to construct a process::Command in a safe way ([; echo "Shell Injection"](https://matklad.github.io/2021/07/30/shell-injection.html)). I also can’t use it to _not_ convert to string (see the sql example [my-desire/it.rs at 1871db5738007bf50ee2243d849312534d9c58e2 · matklad/my-desire · GitHub](https://github.com/matklad/my-desire/blob/1871db5738007bf50ee2243d849312534d9c58e2/tests/it.rs#L28)).

---

<div class="post-metadata">

**Author:** ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)\
**Post date:** [September 22, 2022, 10:35pm UTC](https://internals.rust-lang.org/t/injection-proof-interpolation-without-variadics/17436/4 "2022-09-22T22:35:34Z")

</div>

This would be nice for interpolating `OsString`.

---

<div class="post-metadata">

**Author:** ![jhpratt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jhpratt/32/11640_2.png) [@jhpratt](https://internals.rust-lang.org/u/jhpratt)\
**Post date:** [September 22, 2022, 11:57pm UTC](https://internals.rust-lang.org/t/injection-proof-interpolation-without-variadics/17436/5 "2022-09-22T23:57:56Z")

</div>

True. I was thinking of it solely from the perspective of fitting arbitrary expressions into format strings.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [December 21, 2022, 11:58pm UTC](https://internals.rust-lang.org/t/injection-proof-interpolation-without-variadics/17436/6 "2022-12-21T23:58:05Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
