Borrow, AsRef, Blanket implementation

Migo·2024년 5월 2일
0

Fluent Rust

목록 보기
19/27
post-thumbnail

Borrow

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.

AsRef

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

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

profile
Dude with existential crisis

0개의 댓글

관련 채용 정보