Skip to content

RFC: Dedented String Literals #3830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ae8a3a2
RFC: Dedented String Literals
nik-rev Jun 5, 2025
b98bd7f
fix: remove `#`
nik-rev Jun 5, 2025
5ced05a
fix: add `#`
nik-rev Jun 5, 2025
b4590ac
Clarify why indenting closing quote further is not a syntax error
nik-rev Jun 6, 2025
ac324bd
Relax rules around escaped whitespace characters
nik-rev Jun 6, 2025
9bae185
Move section on crate-provided macros under the "Use a crate instead"…
nik-rev Jun 6, 2025
a589e0d
Explain why a built-in macro would not suffice
nik-rev Jun 6, 2025
50725a6
Add example how the last line of a dedented string could be formatted
nik-rev Jun 6, 2025
14f09de
fix: The arguments to `writeln!`
nik-rev Jun 6, 2025
0c7cb80
fix: use literal escaped `\t`
nik-rev Jun 6, 2025
0e368fc
Line containing the closing quote may include escaped tab `\t` chars
nik-rev Jun 6, 2025
220fac7
fix: minor spelling
nik-rev Jun 6, 2025
dfe1430
fix: spelling
nik-rev Jun 6, 2025
18dd08d
fix: spelling
nik-rev Jun 6, 2025
4730cc8
fix: spelling
nik-rev Jun 6, 2025
8012322
fix: add a newline
nik-rev Jun 6, 2025
70faac7
Add another "sacrifice readability of source code" example with `conc…
nik-rev Jun 6, 2025
545c941
style: fix formatting for all code examples
nik-rev Jun 6, 2025
8b9bcc3
Add section explaining how to have a trailing newline
nik-rev Jun 6, 2025
013a68c
Align all opening quotes with closing quotes. Match `rustfmt`
nik-rev Jun 6, 2025
7a9a58b
Change behaviour of escaped literals: `\t`, `\r` and `\n`
nik-rev Jun 6, 2025
64e289e
fix: Use semicolon
nik-rev Jun 6, 2025
b4dcfd0
fix: Word
nik-rev Jun 6, 2025
c8673ad
Disallow whitespace escapes in the closing line
nik-rev Jun 6, 2025
af7fc31
Fix confusing indentation in example
nik-rev Jun 6, 2025
7f9417c
Remove incorrect description of the chosen acronym
nik-rev Jun 6, 2025
ae9a668
Remove note about injected language into string
nik-rev Jun 7, 2025
b5cdd57
Explain why the letter `d` is the choice, rather than other letters
nik-rev Jun 7, 2025
e36f8ed
Clarify why the dedented string always ends with a newline
nik-rev Jun 7, 2025
c0bddbd
Clarify what is meant by "Whitespace"
nik-rev Jun 7, 2025
8b4422c
Clarify what is meant by an empty line
nik-rev Jun 7, 2025
acda9b2
Add drawback: large string modifier count can be confusing
nik-rev Jun 9, 2025
9de6bcd
Add drawback about `dr"..."` strings
nik-rev Jun 9, 2025
7bbf74b
fix: Spelling
nik-rev Jun 9, 2025
c27b2b7
fix: minor
nik-rev Jun 12, 2025
d0b4c27
fix: wording
nik-rev Jun 12, 2025
1f68236
Prior art: add `inspect.cleandoc` from Python
nik-rev Jun 13, 2025
995efe8
Disambiguate "whitespace" and "newline", use more technical terms
nik-rev Jun 13, 2025
d18a31a
Allow horizontal whitespace characters between the opening quote and …
nik-rev Jun 13, 2025
0a8c747
Disallow mixing spaces and tabs
nik-rev Jun 13, 2025
371910d
fix: indent only with spaces can't give you tabs
nik-rev Jun 13, 2025
a3f9a19
fix: replace tabs with spaces
nik-rev Jun 13, 2025
2eb90a1
Allow mixing spaces and tabs*
nik-rev Jun 13, 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
66 changes: 54 additions & 12 deletions text/3830-dedented-string-literals.md
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ We use these terms throughout the RFC and they are explained in detail in this s
- **Horizontal whitespace** is spaces or tabs. These are the only `Pattern_White_Space` characters that are *horizontal* space per [UAX#31](https://www.unicode.org/reports/tr31/#Contexts_for_Ignorable_Format_Controls)
- **EOL (end-of-line) character** is any "end of line" character as classified in [`UAX#R3a-1`](https://www.unicode.org/reports/tr31/#R3a-1)
- **Indentation** is one or more **horizontal whitespace** at the beginning of a line
- An **empty line** only consists of literal horizontal whitespace

A "newline" is used as an example of a specific EOL character, however any other valid EOL character can be used.

Expand All @@ -797,8 +798,6 @@ Note: **Literal newlines** (*not* escaped newlines: `\n`) are represented with `

## Algorithm for dedented strings

An empty line only consists of literal whitespace (*not* escaped whitespace such as `\n`)

1. The opening line (the line containing the opening quote `"`)
- *May* contain 1 or more horizontal whitespace characters. (*trailing* horizontal whitespace)
- These horizontal whitespace characters are removed.
Expand Down Expand Up @@ -987,6 +986,58 @@ let py = d"

The above example is valid because the invisible characters `U+200F` and `U+200E` after the indentation which will be remain in the output, while the indentation of 4 spaces will be stripped from each line.

## Mixed spaces and tabs

In all examples of this RFC, we only assume that the common indentation of each line (to be stripped) and indentation of the closing quote of the dedented string uses the same character (either literal tabs, or literal spaces)

Mixing these character in a way that is ambiguous is disallowed, and will error. For instance, in the following example with literal tabs (represented by `→`) and literal spaces (represented by `•`) mixed together:

```rust
// error: ambiguous spaces mixed with tabs
let py = d"
→→→→def hello():
→→→→••••print('Hello, world!')

•→••hello()
→→••";
```

The above program is rejected due to ambiguity. The leading indentation must pick a single character.

Choose either **only spaces**:

```rust
let py = d"
••••def hello():
••••••••print('Hello, world!')

••••hello()
••••";
```

Or **only tabs**:

```rust
let py = d"
→→→→def hello():
→→→→→→→→print('Hello, world!')

→→→→hello()
→→→→";
```

Both of the above valid examples would be the same as:

```rust
let py = "\
def hello():
→→→→print('Hello, world!')

hello()";
```

Empty lines can safely be mixed with either spaces or tabs, as they do not count for the purposes of dedentation

# Drawbacks
[drawbacks]: #drawbacks

Expand Down Expand Up @@ -1857,16 +1908,7 @@ In the Rust ecosystem:
# Unresolved questions
[unresolved-questions]: #unresolved-questions

What should happen if we have tabs (represented by `→`) and literal spaces (represented by `•`) mixed together?

```rust
let py = d"
→→→→def hello():
→→→→••••print('Hello, world!')

•→••hello()
→→••";
```
None

# Future possibilities
[future-possibilities]: #future-possibilities
Expand Down