Expressions
Output data from your Rust code into templates with type-safe expressions.
Simple Expressions
Use @ followed by an identifier to output a variable. Values are automatically HTML-escaped for security.
@user.name @* Field access *@
@items.len() @* Method call *@
@items[0] @* Indexing *@
@user.profile.avatar_url @* Nested access *@ Example:
@fn apply(user: User) {
Welcome, @user.name!
Email: @user.email
Member since: @user.created_at.format("%Y")
} Complex Expressions
Use parentheses for inline expressions with operators:
@(a + b) @* Arithmetic *@
@(count * 2 + 1) @* Multi-operator *@
@(value as f64) @* Type cast *@
@(if cond { a } else { b }) @* Inline if *@ Example:
@fn apply(items: Vec- , tax_rate: f64) {
Total items: @items.len()
Page @(current_page + 1) of @total_pages
Tax: $@(subtotal * tax_rate)
} Variable Bindings
Use @let to create local variables within templates.
Operators work directly in @let statements — no parentheses needed. The parser uses greedy parsing to consume the full expression.
@let name = user.name
@let total = a + b @* Operators work directly *@
@let valid = x > 0 && y < 10 @* Boolean operators too *@
@let cast = value as i32 @* Type casts too *@
@let x = a + b; @* Optional semicolon terminator *@ Practical example:
@fn apply(items: Vec- , discount: f64) {
@let subtotal = items.iter().map(|i| i.price).sum::
()
@let discount_amount = subtotal * discount
@let total = subtotal - discount_amount
Subtotal: $@subtotal
Discount: -$@discount_amount
Total: $@total
} Safe (Unescaped) Output
By default, all output is HTML-escaped. Use @safe() for trusted HTML content:
@safe(html_content) @* Output without escaping *@
@safe(markdown_html, "fallback
") @* With fallback value *@ Only use @safe() with content you trust. Never use it with user input.
Example:
@fn apply(article: Article) {
@article.title
@* Title is escaped - safe from XSS *@
@safe(article.rendered_html)
@* rendered_html is trusted, pre-sanitized content *@
} Escaping the @ Symbol
Use @@ to output a literal @ symbol:
@@ @* Outputs: @ *@
email@@example.com @* Outputs: email@example.com *@ Inside @#...#@ raw blocks (any matching number of #), content is output verbatim - no escaping needed. The @@ escape is only needed outside raw blocks to output a literal @.
Macro-like Constructs
These compile-time transforms generate Rust macros. Both @keyword() and keyword() syntax work:
@matches() - Pattern Matching
Compiles to Rust's matches!() macro for inline pattern matching:
@* Simple enum variant matching *@
@if matches(active_tab, ViewProfileTab::ChangePassword) {
Change Password Form
}
@* With or-patterns *@
@if matches(status, Status::Active | Status::Pending) {
Processing...
}
@* With guards *@
@if matches(value, Some(x) if x > 5) {
Value is greater than 5
} @format() - String Formatting
Compiles to Rust's format!() macro:
@format("{} items found", count)
format("{:.2}", price) @* Also works without @ *@
Profile @print() - Concatenation
Concatenates arguments using format!("{}{}", ...):
@print(a, b, c) @* -> format!("{}{}{}", a, b, c) *@
print("Hello, ", name, "!") @* Also works without @ *@ In expression bodies (after = {#{), use format() and print() without the @ prefix:
@fn time_json(items: &Vec- ): String = {
format("[{}]", items.iter().map(|i|
format("{{name:'{}'}}", i.name)
).collect::
>().join(","))
} Expressions in Attributes
Expressions work seamlessly in HTML attributes:
@label
Dynamic classes example:
@fn apply(item: Item) {
@item.title
} Expression Summary
| Syntax | Description | Example |
|---|---|---|
@var | Simple variable (escaped) | @user.name |
@obj.field | Field access | @user.profile.bio |
@obj.method() | Method call | @items.len() |
@(expr) | Complex expression | @(a + b) |
@safe(html) | Unescaped output | @safe(rendered_md) |
@let x = val | Variable binding | @let total = (a + b) |
@matches(expr, pat) | Pattern matching | @if matches(x, Some(_)) |
@format(fmt, args) | String formatting | @@format("{}", val) |
@print(a, b, c) | Concatenation | @print("Hi ", name) |
@@ | Literal @ symbol | email@@example.com |