We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Modules, Visibility, Testing and Docs step 5 of 22
use declarations: nested groups, as, self, and _
A use declaration adds a name to the scope of the module or block it appears
in. The name is an alias for a path such as std::collections::HashMap. It
does not load, run or link any code; it only decides which short names the
code around it can write. This exercise asks you to work out exactly which
names one use declaration creates.
pub fn expand_use(decl: &str) -> Vec<(String, String)>
Parse one use declaration and return every (full_path, bound_name) pair it
creates, sorted. full_path is the complete path the name refers to, and
bound_name is the name it is available under. Malformed input and glob
imports return an empty vec.
use std::collections::HashMap;
-> [("std::collections::HashMap", "HashMap")]
use std::collections::{BTreeSet, hash_map::{self, HashMap}};
-> [("std::collections::BTreeSet", "BTreeSet"),
("std::collections::hash_map", "hash_map"),
("std::collections::hash_map::HashMap", "HashMap")]
use std::io::Write as _;
-> [("std::io::Write", "_")]
In the first example the name is the last segment of the path. In the second,
the outer braces share the prefix std::collections, the inner braces share
std::collections::hash_map, and self names that inner prefix itself, so
one line creates three names. The third example is explained below.
The grammar
- A path is one or more segments separated by
::, such asstd::io::Write. - A leaf is a path, optionally followed by
as name. Its bound name is the alias if there is one, otherwise the path's last segment. - A group is
prefix::{ ... }, a comma-separated list of items inside braces. Each item is itself a leaf or a group, and the prefix is put in front of every item. Groups can nest to any depth, souse std::{fmt, io};creates two names. The prefix may be left out entirely, as inuse {std::fmt, std::io};, which creates the same two names. selfas an item of a group stands for the group's prefix. Inhash_map::{self, HashMap},selfbinds the modulehash_mapunder the namehash_map(or under its alias, as in{self as m}), andHashMapbinds the type.as _imports an item without giving it a usable name. This exercise reports that as the bound name"_".
What as _ is for
To call a trait method with method-call syntax on a concrete type, the trait
has to be in scope. This does not compile without the use line:
use std::io::Write;
fn greet(buf: &mut Vec<u8>) -> std::io::Result<()> {
buf.write_all(b"hi") // error[E0599] without `use std::io::Write`
}
Vec<u8> implements std::io::Write, but write_all is a trait method, and
the compiler only looks for trait methods in traits that are in scope. Traits
in the prelude (such as Clone and Iterator) are always in scope. You also
do not need the import to call a method on a generic parameter bounded by the
trait (W: std::io::Write) or on a dyn std::io::Write.
Importing Write this way also takes the name Write in that scope, which
can clash with another item called Write, such as std::fmt::Write.
use std::io::Write as _; brings the trait's methods into scope without
taking any name. Several as _ imports can sit in the same scope without
conflicting, and code in that scope cannot refer to the trait as Write.
Two lints are relevant here. The default unused_imports warning stays quiet
for use std::io::Write; as long as one of its methods is called, because the
import is doing work. Clippy's unused_trait_names lint, which is in the
allow-by-default restriction group, reports a trait import whose name is
never written and suggests as _. Enable it if your project prefers that
style.
Similarly, use std::string::String; changes nothing, because String is
already in the prelude. unused_imports does not report it, since the name
is used; the allow-by-default redundant_imports lint does.
Contract
- Shape. After trimming leading and trailing whitespace, the input must
start with
use, followed by at least one whitespace character, then one use tree, then;. (Rust also acceptsuse{a, b};with no space; this parser does not.) Whitespace may appear between tokens. Anything else, for example a missing;, a second statement,pub use, or text beforeuse, returns an empty vec. - Segments. Every path segment is an ASCII identifier: a letter or
_followed by letters, digits or_._alone is not a valid segment, and no segment may be empty, so a path cannot start with::. (Rust itself accepts a leading::, as inuse ::std::fmt;, but this parser rejects it.) The parser does not reject Rust keywords such ascrateorfnas names, apart from theselfand_rules here. Returned paths are written with::and no whitespace, even if the input had spaces around::. self. In the full path,selfmay appear only as the first segment (as inuse self::config::Settings;). The itemselfinside a group binds the group's prefix, under the prefix's last segment or under its alias. Aselfitem whose prefix is empty, such asuse self;oruse {self};, is malformed.- Aliases.
astakes one identifier, which may be_. An alias applies only to a leaf:use a::{b} as c;is malformed. - Groups. Braces must balance, and a group must be the last part of its
item:
use a::{b}::c;is malformed. A group may be empty (use a::{};creates no names, so the result is an empty vec) and may end with one trailing comma ({a, b,}). Any other empty item, as in{,}or{a,,b}, is malformed. - Globs. A
*anywhere means a glob import, which this parser does not handle; return an empty vec. - Output. Sort the pairs by full path, then by bound name, comparing
strings byte by byte (so uppercase letters sort before lowercase ones). If
the same pair would be produced twice, include it once. The parser does not
report a name bound twice, whether to the same path or to two different
ones; the real compiler rejects both with E0252. Imports with
as _are the exception, since they take no name.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.