Skip to content

Commit e611fb0

Browse files
committed
website: add if { for {x} } workarounds
1 parent 8cb81d5 commit e611fb0

8 files changed

Lines changed: 395 additions & 0 deletions

File tree

website/docs/concepts/html/conditional-rendering.mdx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,46 @@ html! {
156156
Arms with a single element can omit braces. Arms with multiple children or `let` bindings require braces.
157157

158158
`match` supports all standard Rust patterns including OR-patterns (`A | B`), destructuring, and `if` guards. Exhaustiveness is checked by the Rust compiler.
159+
160+
## Loops inside `if`/`match` bodies
161+
162+
`for`, `while`, and `loop` inside an `if`/`else if`/`else`/`match`-arm body
163+
follow the same rules they do at the top level of `html!`, with one caveat:
164+
if the loop body is fully Rust-parseable (no html elements anywhere inside),
165+
the macro takes the loop as a Rust statement instead of html-control-flow.
166+
167+
The most common way to hit this is a loop whose only child is a bare
168+
`{expr}` block:
169+
170+
```rust , ignore
171+
// Compiles. The for is at the top level, parsed as html-`for`.
172+
html! {
173+
for _ in 0..9 {
174+
{my_foo}
175+
}
176+
}
177+
178+
// Does NOT compile. The for is inside an `if`, parsed as a Rust statement;
179+
// rustc then complains that the body returns a value where `()` is expected.
180+
html! {
181+
if condition {
182+
for _ in 0..9 {
183+
{my_foo}
184+
}
185+
}
186+
}
187+
```
188+
189+
Wrap the child in an html element to keep it as html-`for`:
190+
191+
```rust , ignore
192+
html! {
193+
if condition {
194+
for _ in 0..9 {
195+
<span>{my_foo}</span>
196+
}
197+
}
198+
}
199+
```
200+
201+
See [Lists](./lists.mdx) for the full set of workarounds.

website/docs/concepts/html/lists.mdx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,70 @@ html! {
146146
}
147147
```
148148

149+
:::
150+
151+
:::caution `for` with a bare `{expr}` body inside `if`/`match`/`while`/another `for`
152+
153+
A `for` whose only child is a bare `{expr}` block renders one node per
154+
iteration at the **top level** of an `html!`, but the same code nested
155+
inside an `if`/`match` arm/`while`/another `for` body is rejected with a
156+
`mismatched types: expected (), found T` error. The two positions use
157+
different parsing strategies, and the nested one tries to parse the loop
158+
as a Rust statement first. If the body is fully Rust-parseable (which a
159+
bare `{expr}` block is), it stops being html-control-flow.
160+
161+
```rust , ignore
162+
// Works: the for is at the top level.
163+
html! {
164+
for _ in 0..9 {
165+
{my_foo}
166+
}
167+
}
168+
169+
// Does NOT compile: the for is nested inside an `if`.
170+
html! {
171+
if condition {
172+
for _ in 0..9 {
173+
{my_foo}
174+
}
175+
}
176+
}
177+
```
178+
179+
To render `my_foo` 9 times conditionally, pick one of:
180+
181+
```rust , ignore
182+
// 1. Wrap the child in an html element (recommended).
183+
html! {
184+
if condition {
185+
for _ in 0..9 {
186+
<span>{my_foo}</span>
187+
}
188+
}
189+
}
190+
191+
// 2. Push the for to the top level of a nested `html!`.
192+
html! {
193+
if condition {
194+
{ html! {
195+
for _ in 0..9 {
196+
{my_foo}
197+
}
198+
} }
199+
}
200+
}
201+
202+
// 3. Build the list with an iterator and `collect::<Html>()`.
203+
html! {
204+
if condition {
205+
{ (0..9).map(|_| html!({my_foo})).collect::<Html>() }
206+
}
207+
}
208+
```
209+
210+
The same rule applies inside `match` arms, `while` bodies, and the body
211+
of an outer `for`.
212+
149213
:::
150214

151215
</TabItem>

website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/html/conditional-rendering.mdx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,42 @@ html! {
156156
単一要素のアームはブレースを省略できます。複数の子要素や `let` バインディングを持つアームにはブレースが必要です。
157157

158158
`match` は OR パターン(`A | B`)、分割代入、`if` ガードを含む、すべての標準的な Rust パターンをサポートします。網羅性は Rust コンパイラによってチェックされます。
159+
160+
## `if` / `match` の本体内部のループ
161+
162+
`if` / `else if` / `else` / `match` のアーム本体内部の `for``while``loop` は、`html!` のトップレベル位置と同じ規則に従いますが、一つだけ例外があります:ループ本体が完全に Rust として解析可能な場合(内部のどこにも html 要素がない場合)、マクロはそのループを html の制御フローではなく Rust の文として扱います。
163+
164+
もっとも遭遇しやすいのは、ループ本体に裸の `{expr}` ブロックしか書かれていないケースです:
165+
166+
```rust , ignore
167+
// コンパイル可能。for はトップレベルにあり、html-`for` として解析されます。
168+
html! {
169+
for _ in 0..9 {
170+
{my_foo}
171+
}
172+
}
173+
174+
// コンパイル不可。for は `if` の内部にあり、Rust の文として解析されます。
175+
// その後 rustc が、ループ本体の戻り型が `()` ではないと報告します。
176+
html! {
177+
if condition {
178+
for _ in 0..9 {
179+
{my_foo}
180+
}
181+
}
182+
}
183+
```
184+
185+
子要素を html 要素で囲めば html-`for` の挙動が保たれます:
186+
187+
```rust , ignore
188+
html! {
189+
if condition {
190+
for _ in 0..9 {
191+
<span>{my_foo}</span>
192+
}
193+
}
194+
}
195+
```
196+
197+
回避策の全体像は[リスト](./lists.mdx)を参照してください。

website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/html/lists.mdx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,63 @@ let mut rendered = Vec::new();
9797
- 末尾に `;` を付けてプリアンブルの式文にします:`<Type>::method(...);`。戻り値は破棄されます。
9898
- `{...}` で囲んで戻り値をノードとして使います:`{ <Type>::method(...) }`
9999

100+
:::
101+
102+
:::caution `if` / `match` / `while` / 外側の `for` の内部で本体が裸の `{expr}` だけの `for`
103+
104+
`for` ループの本体に裸の `{expr}` ブロックしか書かれていない場合、`html!`**トップレベル**にあれば反復ごとに 1 ノードずつ描画されます。ところが同じコードを `if` / `match` のアーム / `while` / 外側の `for` の本体に入れ子で置くと、`mismatched types: expected (), found T` というエラーで拒否されます。トップレベルと入れ子の位置とでは解析方針が異なり、入れ子の位置ではループをまず Rust の文として解析しようとします。本体が完全に Rust として解析可能であれば(裸の `{expr}` ブロックはまさにそうです)、html の制御フローとして扱われなくなります。
105+
106+
```rust , ignore
107+
// コンパイル可能:for はトップレベルにあります。
108+
html! {
109+
for _ in 0..9 {
110+
{my_foo}
111+
}
112+
}
113+
114+
// コンパイル不可:for は `if` の内部に入れ子になっています。
115+
html! {
116+
if condition {
117+
for _ in 0..9 {
118+
{my_foo}
119+
}
120+
}
121+
}
122+
```
123+
124+
`my_foo` を条件付きで 9 回描画したい場合は、次のいずれかを選んでください:
125+
126+
```rust , ignore
127+
// 1. 子要素を html 要素で囲む(推奨)。
128+
html! {
129+
if condition {
130+
for _ in 0..9 {
131+
<span>{my_foo}</span>
132+
}
133+
}
134+
}
135+
136+
// 2. for をネストした `html!` のトップレベルへ移す。
137+
html! {
138+
if condition {
139+
{ html! {
140+
for _ in 0..9 {
141+
{my_foo}
142+
}
143+
} }
144+
}
145+
}
146+
147+
// 3. イテレータと `collect::<Html>()` でリストを組み立てる。
148+
html! {
149+
if condition {
150+
{ (0..9).map(|_| html!({my_foo})).collect::<Html>() }
151+
}
152+
}
153+
```
154+
155+
同じ規則は `match` のアーム本体、`while` の本体、外側の `for` の本体にも当てはまります。
156+
100157
:::
101158

102159
</TabItem>

website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/html/conditional-rendering.mdx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,42 @@ html! {
156156
单元素 arm 可以省略大括号。含多个子节点或 `let` 绑定的 arm 需要大括号。
157157

158158
`match` 支持所有标准 Rust 模式,包括 OR 模式(`A | B`)、解构和 `if` 守卫。穷尽性由 Rust 编译器检查。
159+
160+
## `if` / `match` 体内部的循环
161+
162+
`if` / `else if` / `else` / `match` 分支体内部的 `for``while``loop``html!` 顶层位置的规则相同,但有一个例外:如果循环体可以被 Rust 完整解析(内部任何位置都没有 html 元素),宏会把这个循环视为 Rust 语句而不是 html 控制流。
163+
164+
最常见的触发情形是循环体里只有一个裸 `{expr}` 块:
165+
166+
```rust , ignore
167+
// 可以编译。for 在最外层,被解析为 html-`for`。
168+
html! {
169+
for _ in 0..9 {
170+
{my_foo}
171+
}
172+
}
173+
174+
// 无法编译。for 在 `if` 内部,被解析为 Rust 语句;
175+
// 之后 rustc 会报告循环体返回的类型不是 `()`。
176+
html! {
177+
if condition {
178+
for _ in 0..9 {
179+
{my_foo}
180+
}
181+
}
182+
}
183+
```
184+
185+
把子节点用 html 元素包起来即可保留 html-`for` 行为:
186+
187+
```rust , ignore
188+
html! {
189+
if condition {
190+
for _ in 0..9 {
191+
<span>{my_foo}</span>
192+
}
193+
}
194+
}
195+
```
196+
197+
完整的解决方案集合请参见[列表](./lists.mdx)

website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/html/lists.mdx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,63 @@ let mut rendered = Vec::new();
9797
- 在末尾加上 `;`,使其成为前置的表达式语句:`<Type>::method(...);`。返回值会被丢弃。
9898
-`{...}` 包起来,将返回值作为节点使用:`{ <Type>::method(...) }`
9999

100+
:::
101+
102+
:::caution 在 `if` / `match` / `while` / 外层 `for` 内部使用 `{expr}` 作为 `for` 循环体
103+
104+
如果 `for` 循环体只包含一个裸 `{expr}` 块,在 `html!`**最外层**位置可以正常工作,每次迭代渲染一个节点;但同样的代码嵌套在 `if` / `match` 分支 / `while` / 另一个 `for` 体内时会被拒绝,并报告 `mismatched types: expected (), found T`。这两个位置使用的解析策略不同:嵌套位置会先尝试把循环解析为 Rust 语句。如果循环体能完全被 Rust 解析(裸 `{expr}` 块就是这种情况),它就不再被视为 html 控制流。
105+
106+
```rust , ignore
107+
// 可以编译:for 位于最外层。
108+
html! {
109+
for _ in 0..9 {
110+
{my_foo}
111+
}
112+
}
113+
114+
// 无法编译:for 嵌套在 `if` 内部。
115+
html! {
116+
if condition {
117+
for _ in 0..9 {
118+
{my_foo}
119+
}
120+
}
121+
}
122+
```
123+
124+
要在条件成立时把 `my_foo` 渲染 9 次,请选择以下任一方式:
125+
126+
```rust , ignore
127+
// 1. 用 html 元素包裹子节点(推荐)。
128+
html! {
129+
if condition {
130+
for _ in 0..9 {
131+
<span>{my_foo}</span>
132+
}
133+
}
134+
}
135+
136+
// 2. 把 for 放进嵌套的 `html!` 顶层。
137+
html! {
138+
if condition {
139+
{ html! {
140+
for _ in 0..9 {
141+
{my_foo}
142+
}
143+
} }
144+
}
145+
}
146+
147+
// 3. 用迭代器配合 `collect::<Html>()` 构建列表。
148+
html! {
149+
if condition {
150+
{ (0..9).map(|_| html!({my_foo})).collect::<Html>() }
151+
}
152+
}
153+
```
154+
155+
同样的规则也适用于 `match` 分支、`while` 循环体以及外层 `for` 的循环体。
156+
100157
:::
101158

102159
</TabItem>

website/i18n/zh-Hant/docusaurus-plugin-content-docs/current/concepts/html/conditional-rendering.mdx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,42 @@ html! {
156156
單元素 arm 可以省略大括號。含多個子節點或 `let` 綁定的 arm 需要大括號。
157157

158158
`match` 支援所有標準 Rust 模式,包括 OR 模式(`A | B`)、解構和 `if` 守衛。窮舉性由 Rust 編譯器檢查。
159+
160+
## `if` / `match` 主體內部的迴圈
161+
162+
`if` / `else if` / `else` / `match` 分支主體內部的 `for``while``loop``html!` 最外層位置的規則相同,但有一個例外:如果迴圈主體可以被 Rust 完整剖析(內部任何位置都沒有 html 元素),巨集會把這個迴圈視為 Rust 陳述式而不是 html 控制流。
163+
164+
最常見的觸發情形是迴圈主體裡只有一個裸 `{expr}` 區塊:
165+
166+
```rust , ignore
167+
// 可以編譯。for 在最外層,被解析為 html-`for`。
168+
html! {
169+
for _ in 0..9 {
170+
{my_foo}
171+
}
172+
}
173+
174+
// 無法編譯。for 在 `if` 內部,被解析為 Rust 陳述式;
175+
// 之後 rustc 會回報迴圈主體傳回的型別不是 `()`。
176+
html! {
177+
if condition {
178+
for _ in 0..9 {
179+
{my_foo}
180+
}
181+
}
182+
}
183+
```
184+
185+
把子節點用 html 元素包起來即可保留 html-`for` 行為:
186+
187+
```rust , ignore
188+
html! {
189+
if condition {
190+
for _ in 0..9 {
191+
<span>{my_foo}</span>
192+
}
193+
}
194+
}
195+
```
196+
197+
完整的解決方法集合請參見[清單](./lists.mdx)

0 commit comments

Comments
 (0)