This adds strictly more information to the source files and reduces the need for customized tooling to render the book. (While this should not change the output of _rustbook_, it is very useful when rendering the sources with external tools like Pandoc.) This only adds the language marker to "first level" code blocks (and not to code blocks in comments inside of code examples). r? @steveklabnik
2.6 KiB
% Borrow and AsRef
The Borrow
and AsRef
traits are very similar, but
different. Here’s a quick refresher on what these two traits mean.
Borrow
The Borrow
trait is used when you’re writing a datastructure, and you want to
use either an owned or borrowed type as synonymous for some purpose.
For example, HashMap
has a get
method which uses Borrow
:
fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where K: Borrow<Q>,
Q: Hash + Eq
This signature is pretty complicated. The K
parameter is what we’re interested
in here. It refers to a parameter of the HashMap
itself:
struct HashMap<K, V, S = RandomState> {
The K
parameter is the type of key the HashMap
uses. So, looking at
the signature of get()
again, we can use get()
when the key implements
Borrow<Q>
. That way, we can make a HashMap
which uses String
keys,
but use &str
s when we’re searching:
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("Foo".to_string(), 42);
assert_eq!(map.get("Foo"), Some(&42));
This is because the standard library has impl Borrow<str> for String
.
For most types, when you want to take an owned or borrowed type, a &T
is
enough. But one area where Borrow
is effective is when there’s more than one
kind of borrowed value. This is especially true of references and slices: you
can have both an &T
or a &mut T
. If we wanted to accept both of these types,
Borrow
is up for it:
use std::borrow::Borrow;
use std::fmt::Display;
fn foo<T: Borrow<i32> + Display>(a: T) {
println!("a is borrowed: {}", a);
}
let mut i = 5;
foo(&i);
foo(&mut i);
This will print out a is borrowed: 5
twice.
AsRef
The AsRef
trait is a conversion trait. It’s used for converting some value to
a reference in generic code. Like this:
let s = "Hello".to_string();
fn foo<T: AsRef<str>>(s: T) {
let slice = s.as_ref();
}
Which should I use?
We can see how they’re kind of the same: they both deal with owned and borrowed versions of some type. However, they’re a bit different.
Choose Borrow
when you want to abstract over different kinds of borrowing, or
when you’re building a datastructure that treats owned and borrowed values in
equivalent ways, such as hashing and comparison.
Choose AsRef
when you want to convert something to a reference directly, and
you’re writing generic code.