Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fb7954e
Add draft chapter on the visitor pattern
tgiannak Jun 19, 2025
eef60ac
Add draft chapter on PIMPL
tgiannak Jun 20, 2025
539490c
fix a typo in exceptions.md
spacewander Jun 20, 2025
7d6ad11
Correct statement about extended visitor pattern in Rust and RTTI
tgiannak Jun 23, 2025
e06ce5f
Add draft chapter on RVO and NRVO
tgiannak Jun 24, 2025
55e4564
Add draft chapter on placement new
tgiannak Jun 24, 2025
c5d85bb
Fix typo in type equivalents chapter
tgiannak Jun 24, 2025
e911061
Remove content on separate construction and initialization
tgiannak Jun 24, 2025
cc7a69a
Remove low-priority chapters that we aren't going to finish
tgiannak Jun 24, 2025
27203ea
Add chapter on FFI that links to the Rustonomicon and other sources
tgiannak Jun 24, 2025
9c559d1
Add draft chapter on build systems
tgiannak Jun 24, 2025
223de14
Write "e.g." for C++ tools in chapter names
tgiannak Jun 25, 2025
5ed1fda
Add draft chapter on documentation tools
tgiannak Jun 25, 2025
e442908
Rename file to match convention used for other chapters
tgiannak Jun 27, 2025
8ab731f
Remove empty, unused chapter files
tgiannak Jun 27, 2025
70d86c7
Fix filename in SUMMARY
tgiannak Jun 27, 2025
fc384b9
Fix line about downcasting in extended visitor pattern
tgiannak Jun 27, 2025
8aa1607
Add a quiz question for the visitors chapter
tgiannak Jun 27, 2025
a9c90c7
Add a quiz question for the adapter pattern chapter
tgiannak Jun 27, 2025
30f5c47
Add a quiz question for the iterators chapter
tgiannak Jun 27, 2025
9b2ff82
Add a second quiz question for the iterators chapter
tgiannak Jun 27, 2025
6d36123
Add two quiz questions for the closures chapter
tgiannak Jun 27, 2025
d31b631
Add quiz question for expected errors chapter
tgiannak Jun 27, 2025
0a9c65f
Add two questions to the chapter on handling errors from bugs
tgiannak Jun 27, 2025
0a79ba6
Add second quiz question for expected errors chapter
tgiannak Jun 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
- [Default constructors](./idioms/constructors/default_constructors.md)
- [Copy and move constructors](./idioms/constructors/copy_and_move_constructors.md)
- [Rule of three/five/zero](./idioms/constructors/rule_of_three_five_zero.md)
<!-- - [Separate construction and initialization](./idioms/constructors/partial_initialzation.md) -->
- [Destructors and resource cleanup](./idioms/destructors.md)
- [Data modeling](./idioms/data_modeling.md)
- [Abstract classes, interfaces, and dynamic dispatch](./idioms/data_modeling/abstract_classes.md)
Expand Down Expand Up @@ -43,26 +42,24 @@
- [Multiple return values](./idioms/out_params/multiple_return.md)
- [Optional return values](./idioms/out_params/optional_return.md)
- [Pre-allocated buffers](./idioms/out_params/pre-allocated_buffers.md)
- [Varargs]()
- [Attributes]()
- [Calling C (FFI)]()
- [NRVO, RVO, and placement new]()
- [Rust and C++ interoperability (FFI)](./idioms/ffi.md)
- [NRVO and RVO](./idioms/rvo.md)
- [Placement new](./idioms/placement_new.md)
- [Concurrency (threads and async)]()

# Patterns

- [Adapter pattern](./patterns/adapter.md)
- [Visitor pattern and double dispatch]()
- [Visitor pattern and double dispatch](./patterns/visitor.md)
- [Curiously recurring template pattern (CRTP)](./patterns/crtp.md)
- [Pointer-to-implementation (PImpl)]()
- [X macros]()
- [Pointer-to-implementation (PIMPL)](./patterns/pimpl.md)

# Ecosystem

- [Libraries](./etc/libraries.md)
- [Tests](./etc/tests.md)
- [Documentation (Doxygen)]()
- [Build systems (CMake)]()
- [Documentation (e.g., Doxygen)](./etc/documentation.md)
- [Build systems (e.g., CMake)](./etc/build_systems.md)

---

Expand Down
44 changes: 44 additions & 0 deletions src/etc/build_systems.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Build systems (e.g., CMake)

One major difference between the C++ and Rust ecosystems is that C++ and C
libraries tend to be either provided by OS distributions or be included in the
repository for a project, while Rust has a central language-specific package
registry called [crates.io](https://crates.io/).

This difference is amplified by the fact that the Rust build tool, Cargo, has a
build in package manager that works with crates.io, private registries, local
packages, and vendored sources.

Cargo is documented in detail in the [Cargo
Book](https://doc.rust-lang.org/cargo/).

## Packages for C and C++ system libraries

Many C libraries have crates on crates.io providing both low-level bindings and
high-level safe Rust abstractions. For example, for the libgit2 library there is
both a low-level [libgit2-sys crate](https://crates.io/crates/libgit2-sys) and a
high-level [git2 crate](https://crates.io/crates/git2). See the [chapter on the
Rust FFI](../idioms/ffi.md) for more information on how to define these crates.

## Building C, C++, and Rust code

Cargo [build
scripts](https://doc.rust-lang.org/cargo/reference/build-script-examples.html)
can be used to build C and C++ code as part of a Rust project. The linked
chapter of the Cargo book includes links to resources handling the compilation
of C, C++, and other code, working with `pkg-config`, etc.

## Testing (CTest)

Cargo includes support for [running
tests](https://doc.rust-lang.org/cargo/guide/tests.html).

## Packaging for distribution (CPack)

Unlike CPack which is provided with CMake, Cargo does not come with tools for
packaging for distribution to end users. However, there are third party Cargo
helpers for packaging, such as [cargo-deb](https://crates.io/crates/cargo-deb)
for creating Debian package,
[cargo-generate-rpm](https://crates.io/crates/cargo-generate-rpm) for creating
RPM packages, and [cargo-wix](https://crates.io/crates/cargo-wix) for creating
Windows installers.
198 changes: 198 additions & 0 deletions src/etc/documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# Documentation (e.g., Doxygen)

While C++ has several documentation tools, such as Doxygen and Sphinx, Rust has
a single documentation tool,
[Rustdoc](https://doc.rust-lang.org/rustdoc/index.html). Rustdoc is supported by
[`docs.rs`](https://docs.rs/), cargo,
[rust-analyzer](https://rust-analyzer.github.io/), and is the tool used for
documenting [the standard
library](https://doc.rust-lang.org/std/vec/struct.Vec.html#blanket-implementations).
Rustdoc is installed by default with the Rust toolchain for most distributions.

The features and options available for Rustdoc are documented in the [Rustdoc
Book](https://doc.rust-lang.org/rustdoc/). The book also documents [best
practices for documenting Rust
code](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html), which
differ slightly from the recommended practices for documenting C++ code using
Doxygen.

The Cargo integration is documented at in the Cargo book under the [`cargo
doc`](https://doc.rust-lang.org/cargo/commands/cargo-doc.html) and [`cargo
rustdoc`](https://doc.rust-lang.org/cargo/commands/cargo-rustdoc.html) commands,
as well as in the [doctests
section](https://doc.rust-lang.org/cargo/commands/cargo-test.html#documentation-tests)
of the `cargo test` command.

This chapter compares some aspects of Rustdoc with Doxygen in order to help with
understanding what to expect when using Rustdoc when coming from Doxygen or
similar C++ documentation tools.

## Output formats

Unlike Doxygen which can also produce PDF and man page output, Rustdoc only
produces HTML output. The produced documentation does include client-side
searching, which includes the ability to search by type signature.

The [rust-analyzer](https://rust-analyzer.github.io/) language server supports
Rustdoc comments, and makes them available to editors with language server
protocol support on hover, even for Rustdoc comments in the current project.

## Rustdoc comment syntax

Unlike Doxygen, which has several supported comment syntaxes for C++, Rustup
supports a single comment syntax. Comments beginning with `//!` document the
top-level module or crate. Comments beginning with `///` document the following
item.

<div class="comparison">

```cpp
/**
* @file myheader.h
* @brief A description of this file.
*
* A longer description, with examples, etc.
*/

/**
* @brief A description of this class.
*
* A longr description, with examples, etc.
*/
struct MyClass {
// ...
};
```

```rust
//! A description of this module or crate.
//!
//! A longer description, with examples, etc.

/// A description of this type.
///
/// A longer description, with examples, etc.
struct MyClass {
// ...
}
```

</div>

### Special forms

The content of the comment up until the first blank line is treated similarly to
the `@brief` form in Doxygen.

Aside from that, Rustdoc does not have special forms for documenting various
parts of an item, such as the parameters of a function. Instead, [Markdown
syntax can be
used](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html#markdown)
to format the documentation, which is otherwise given in prose.

There are several common conventions used for structuring documentation
comments. The most common convention is to include sections (defined using
Markdown header syntax) for whichever of the following are necessary for the
item being documented:

- panics (for functions that panic, e.g., on
[`Vec::split_at`](https://doc.rust-lang.org/std/vec/struct.Vec.html#panics-32)),
- safety (for unsafe functions, e.g., on
[`Vec::split_at_unchecked`](https://doc.rust-lang.org/std/primitive.slice.html#safety-10)),
and
- examples (e.g., on [`Vec::split_at`](https://doc.rust-lang.org/std/vec/struct.Vec.html#examples-105)).

The following comment compares documentation for a C++ function using Doxygen to
documentation for a Rust function using Rustdoc.

<div class="comparison">
<a id="special-forms-comparison"></a>

```cpp
/**
* @brief Computes the factorial.
*
* Computes the factorial in a stack-safe way.
* The factorial is defined as...
*
* @code
* #include <cassert>
* #include "factorial.h"
*
* int main() {
* int res = factorial(3);
* assert(6 == res);
* }
* @endcode
*
* @param n The number of which to take the factorial
*
* @return The factorial
*
* @exception domain_error If n < 0
*/
int factorial(int n);
```

```rust
/// Computes the factorial.
///
/// Computes the factorial in a stack-safe way.
/// The factorial is defined as...
///
/// # Examples
///
/// ```
/// let res = factorial(3);
/// assert_eq!(6, res);
/// ```
///
/// # Panics
///
/// Requires that `n >= 0`, otherwise panics.
/// For the non-panicking version see
/// [`factorial_checked`].
fn factorial(n: i32) -> i32 {
// ...
# todo!()
}
```

<div>

### Automatic documentation

Many of the things that can be derived from the code are automatically included
by Rustdoc. A major one is that trait implementations (e.g., on
[`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html#trait-implementations)),
including blanket implementations (e.g., on
[`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html#blanket-implementations)),
for a type do not have to be documented manually because implementations that
are visible in the crate are automatically discovered and included by Rustdoc.

# Additional features

Some valuable Rustdoc features may not be expected by someone coming from using
Doxygen. Because those features provide significant benefit, they are pointed
out here.

## Doctest support via Rustdoc and `cargo test`

One specific benefit of including examples when documenting Rust programs using
Rustdoc is that [the examples can be included in the test suite when running
`cargo
test`](https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html).

The handling of examples as tests in Rustdoc includes logic for handling partial
programs, so that even [the code example in the earlier
comparison](#special-forms-comparison) can serve as a test.

## Local documentation for project and installed libraries

Local documentation for both the working project and dependent libraries can be
viewed in-browser using `cargo doc --open`. Private items for the project can be
included in the documentation by using `cargo doc --open
--document-private-items`. Because Rustdoc comments are also used by the
[rust-analyzer](https://rust-analyzer.github.io/) language server to provide
documentation on hover in compatible editors, it is often worth it to document
private items using Rustdoc comments.
2 changes: 2 additions & 0 deletions src/idioms/closures.md
Original file line number Diff line number Diff line change
Expand Up @@ -1429,3 +1429,5 @@ closure. The example involving `FnOnce` functions in [the previous
section](#closures-ownership-and-fnonce) may be a point of frustration
initially, but the behavior has the benefit of reducing the documentation and reasoning
burdens.

{{#quiz closures.toml}}
80 changes: 80 additions & 0 deletions src/idioms/closures.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
[[questions]]
type = "MultipleChoice"
prompt.prompt = """
Does the following Rust program compile? If not, why not?

```rust
fn main() {
let s = String::from("hi");

for x in (0..10).map(|_| s) {
println!("{}", x);
}
}
```
"""
prompt.distractors = [
# Thinking in terms of C copy semantics.
"""
The program compiles.
""",
# Thinking that `FnOnce` has to do with the number of calls rather than
# ownership.
"""
The program does not compile because `|_| s` is a `FnOnce` closure, but is
called 10 times because the range is `0..10`.
"""
]
answer.answer = """
The program does not compile because the `|_| s` is a `FnOnce` closure, but
`map` takes a `FnMut` closure.
"""
context = """
The compiler will actually infer the equivalent problem that the closure is
`FnMut` and then state that captured variables cannot be moved out of a `FnMut`
closure.

The way to fix the problem depends on whether the closure needs to return owned
`String` values, or if `&str` is enough. In the latter case, the closure can be
`|_| &s` instead. In the former case, the closure needs to return a clone of
`s`.
"""
id = "15d61c38-0bd1-45eb-bb2f-db9d357995e3"

[[questions]]
type = "MultipleChoice"
prompt.prompt = """
Does the following Rust program compile? If not, why not?

```rust
fn main() {
let n: i32 = 42;

for x in (0..10).map(|_| n) {
println!("{}", x);
}
}
```
"""
prompt.distractors = [
# Not seeing the difference with the previous question.
"""
The program does not compile because the `|_| s` is a `FnOnce` closure, but
`map` takes a `FnMut` closure.
""",
# Thinking that `FnOnce` has to do with the number of calls rather than
# ownership.
"""
The program does not compile because `|_| s` is a `FnOnce` closure, but is
called 10 times because the range is `0..10`.
"""
]
answer.answer = """
The program compiles.
"""
context = """
Because `i32` implements the `Copy` trait, returning `n` implicitly copies `n`,
making the closure a `Fn` closure instead of a `FnOnce` closure. `Fn` closures
implement `FnMut`, and so can be used with `map`.
"""
id = "798f623e-0802-4b29-8516-09d443dbb7e8"
Loading
Loading