See the following example.
#[derive(Debug)]
struct MyStruct {
my_field: i32,
}
fn func_that_takes_borrow(arg: impl Borrow<MyStruct>) {
println!("{:?}", arg.borrow());
}
Simple. All it does is just print out MyStruct
with its argument abstracting away Borrow
. Let's see the trait in more detail.
pub trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
}
Again, simple enough to understand! Over arbitrary type Borrowed
, it simply returns a reference.
Here is interesting thing. You can invoke the function that takes argument that implements borrow with references as well as moved ones.
What it means is, it enables the following:
let my_struct = MyStruct { my_field: 42 };
func_that_takes_borrow(&my_struct); // It takes reference
func_that_takes_borrow(my_struct); // But it also takes owned one!
One of the questions I get frequently asked at this point is Doesn't violate ownership rule?
Well, ownership rule is NOT about how consumer uses the argument but rather how value is passed(how value is injected.) So it has nothing to do with ownership.
Let's compare this to AsRef
which has very similar method signature except for the name.
fn func_that_takes_as_ref(arg: impl AsRef<MyStruct>) {
println!("{:?}", arg.as_ref().my_field);
}
let my_struct = MyStruct { my_field: 42 };
func_that_takes_as_ref(&my_struct); // Error! the trait bound `MyStruct: std::convert::AsRef<MyStruct>` is not satisfied
func_that_takes_as_ref(my_struct); // Error! the trait bound `MyStruct: std::convert::AsRef<MyStruct>` is not satisfied
Both error out. Let's see the trait declaration.
pub trait AsRef<T: ?Sized> {
fn as_ref(&self) -> &T;
}
As I said, not much different. Then, why it shows so much difference?
It is because of what we call Blacket implementation
Making sense of the correct behaviour of a certain trait requires thorough investigation in Rust; so is the difference between AsRef
and Borrow
, what set them apart is the following implemenation:
impl<T: ?Sized> Borrow<T> for T {
fn borrow(&self) -> &T {
self
}
}
impl<T: ?Sized> BorrowMut<T> for T {
fn borrow_mut(&mut self) -> &mut T {
self
}
impl<T: ?Sized> Borrow<T> for &T {
fn borrow(&self) -> &T {
&**self
}
}
impl<T: ?Sized> Borrow<T> for &mut T {
fn borrow(&self) -> &T {
&**self
}
}
Some of you may find this a bit daunting but what it offers is very simple. For any type T and the reference to T(denoted as &T
), Borrow
is implemented. And for any type T and the mutable reference to T(denoted as &mut T
), BorrowMut
is implemented; two blacket implemenations that are not for AsRef
or AsMut