Return sub-type, or use nested closures?
⚓ Rust 📅 2026-02-15 👤 surdeus 👁️ 1I'm building a framework for generating docs, in which there's a trait Generator that takes in displayable items and generates the docs in its format.
There is a need for nesting items (e.g. links with text, lists, tables), and I'm trying to decide between two patterns. My first option is to return a Nest type that exclusively borrows the generator, which you can then use just like the original generator. Some pseudocode:
let mut g = ExampleMarkdownGenerator::new();
g.add("h1", "Hello, World!");
// mutably borrows `g`, so you can't add to it until
// `list` goes out of scope with `end`
let mut list = g.nest("ul");
list.add("p", "Item 1");
let mut bold = list.nest("b");
bold.add("p", "This is bold");
bold.end(); // we can use `list` again here
let mut list2 = list.nest("ul");
// and so on...
list2.end();
list.end();
// now, and only now, can we use the original `g` again.
Option two is to use closures to provide nesting. That might look like this:
let mut g = ExampleMarkdownGenerator::new();
g.add("h1", "Hello, World!");
g.nest("ul", |g| {
g.add("p", "Item 1");
g.nest("b", |g| {
g.add("p", "This is bold");
}); // bold ends here
g.nest("ul", |g| {
// and so on...
});
}); // list ends here
The closure form is theoretically cleaner, by far (it avoids nasty issues around e.g. forgetting to explicitly end a nest); however, a major goal of this library is ergonomics, and I worry a little that the excessive closure nesting could become annoying with scopes and indentation.
So, what do you all think?
(Also, I'm sure the first technique has a name, but I can't think of it. If anyone knows, I'd appreciate a pointer to some existing literature on it.
)
1 post - 1 participant
🏷️ Rust_feed