2

I've implemented AsRef<[u8]> for my type and the automatic conversion to &[u8] using .as_ref() works but the &-operator doesn't... how can I make the operator work?

struct Buffer {
    data: Vec<u8>,
}

impl AsRef<[u8]> for Buffer {
    fn as_ref(&self) -> &[u8] {
        &self.data
    }
}

fn print_slice(slice: &[u8]) {
    slice.iter().for_each(|b| print!("{:02x}", b));
    println!()
}

fn main() {
    let buffer = Buffer {
        data: b"Testolope".to_vec(),
    };
    // print_slice(&buffer);     // <- This does not work
    print_slice(buffer.as_ref()) // <- This works
}
error[E0308]: mismatched types
  --> src/main.rs:20:17
   |
20 |     print_slice(&buffer);
   |                 ^^^^^^^ expected slice, found struct `Buffer`
   |
   = note: expected type `&[u8]`
              found type `&Buffer`

I want a generic solution. Other datatypes like Vec<u8> support the conversion to &[u8] by using the &-operator. It would be cool if I could make this work for my own types so that I don't have to use .as_ref() every time.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
K. Biermann
  • 1,295
  • 10
  • 22

2 Answers2

4

You cannot overload the reference operator, but you can hook into Deref coercion:

impl std::ops::Deref for Buffer {
    type Target = [u8];

    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

Your original code works in this case.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • This is what I was looking for; thanks for the links! – K. Biermann May 15 '18 at 15:52
  • @Shepmaster, can you explain why after implementing as you show above calling `&buffer` is equivalent to `&buffer.data`? I would expect to have an error with `&buffer.data` because implementing deref returns the `data` field already, so it is essentially calling `(buffer.data).data`. See playground: https://play.rust-lang.org/?gist=ebcece3fb7a076c5d8151e94e0c16f0e&version=stable&mode=debug – stevensonmt May 15 '18 at 16:38
  • 1
    @MatthewStevenson 1. it's precedence is `&(buffer.data)` 2. deref coercion only kicks in at a [coercion site](https://doc.rust-lang.org/nomicon/coercions.html) and field access isn't one of them, so `&((&buffer).data)` also works. – Shepmaster May 15 '18 at 17:08
1

You are calling the whole struct with print_slice(&buffer); but only the data field with print_slice(buffer.as_ref()). If you make it print_slice(&buffer.data) it will work. Alternatively change the type signature of the print_slice function to expect &Buffer, which will make the as_ref() line not work.

stevensonmt
  • 702
  • 1
  • 6
  • 19
  • Sry, my question was no clear enough: I want a generic solution. Other datatypes like `Vec` support the conversion to `&[u8]` by using the `&`-operator. Now it would be cool if I could implement this for my own types so that I don't have to use `.as_ref()` every time. – K. Biermann May 15 '18 at 15:34