Function textwrap::wrap

source ·
pub fn wrap<'a, Opt>(text: &str, width_or_options: Opt) -> Vec<Cow<'_, str>>
where Opt: Into<Options<'a>>,
Expand description

Wrap a line of text at a given width.

The result is a vector of lines, each line is of type Cow<'_, str>, which means that the line will borrow from the input &str if possible. The lines do not have trailing whitespace, including a final '\n'. Please use fill() if you need a String instead.

The easiest way to use this function is to pass an integer for width_or_options:

use textwrap::wrap;

let lines = wrap("Memory safety without garbage collection.", 15);
assert_eq!(lines, &[
    "Memory safety",
    "without garbage",
    "collection.",
]);

If you need to customize the wrapping, you can pass an Options instead of an usize:

use textwrap::{wrap, Options};

let options = Options::new(15)
    .initial_indent("- ")
    .subsequent_indent("  ");
let lines = wrap("Memory safety without garbage collection.", &options);
assert_eq!(lines, &[
    "- Memory safety",
    "  without",
    "  garbage",
    "  collection.",
]);

§Optimal-Fit Wrapping

By default, wrap will try to ensure an even right margin by finding breaks which avoid short lines. We call this an “optimal-fit algorithm” since the line breaks are computed by considering all possible line breaks. The alternative is a “first-fit algorithm” which simply accumulates words until they no longer fit on the line.

As an example, using the first-fit algorithm to wrap the famous Hamlet quote “To be, or not to be: that is the question” in a narrow column with room for only 10 characters looks like this:

To be, or
not to be:
that is
the
question

Notice how the second to last line is quite narrow because “question” was too large to fit? The greedy first-fit algorithm doesn’t look ahead, so it has no other option than to put “question” onto its own line.

With the optimal-fit wrapping algorithm, the previous lines are shortened slightly in order to make the word “is” go into the second last line:

To be,
or not to
be: that
is the
question

Please see WrapAlgorithm for details on the choices.

§Examples

The returned iterator yields lines of type Cow<'_, str>. If possible, the wrapped lines will borrow from the input string. As an example, a hanging indentation, the first line can borrow from the input, but the subsequent lines become owned strings:

use std::borrow::Cow::{Borrowed, Owned};
use textwrap::{wrap, Options};

let options = Options::new(15).subsequent_indent("....");
let lines = wrap("Wrapping text all day long.", &options);
let annotated = lines
    .iter()
    .map(|line| match line {
        Borrowed(text) => format!("[Borrowed] {}", text),
        Owned(text) => format!("[Owned]    {}", text),
    })
    .collect::<Vec<_>>();
assert_eq!(
    annotated,
    &[
        "[Borrowed] Wrapping text",
        "[Owned]    ....all day",
        "[Owned]    ....long.",
    ]
);

§Leading and Trailing Whitespace

As a rule, leading whitespace (indentation) is preserved and trailing whitespace is discarded.

In more details, when wrapping words into lines, words are found by splitting the input text on space characters. One or more spaces (shown here as “␣”) are attached to the end of each word:

"Foo␣␣␣bar␣baz" -> ["Foo␣␣␣", "bar␣", "baz"]

These words are then put into lines. The interword whitespace is preserved, unless the lines are wrapped so that the "Foo␣␣␣" word falls at the end of a line:

use textwrap::wrap;

assert_eq!(wrap("Foo   bar baz", 10), vec!["Foo   bar", "baz"]);
assert_eq!(wrap("Foo   bar baz", 8), vec!["Foo", "bar baz"]);

Notice how the trailing whitespace is removed in both case: in the first example, "bar␣" becomes "bar" and in the second case "Foo␣␣␣" becomes "Foo".

Leading whitespace is preserved when the following word fits on the first line. To understand this, consider how words are found in a text with leading spaces:

"␣␣foo␣bar" -> ["␣␣", "foo␣", "bar"]

When put into lines, the indentation is preserved if "foo" fits on the first line, otherwise you end up with an empty line:

use textwrap::wrap;

assert_eq!(wrap("  foo bar", 8), vec!["  foo", "bar"]);
assert_eq!(wrap("  foo bar", 4), vec!["", "foo", "bar"]);