By default, all generic types, T here included, are required to be Sized. Something must be Sized in order to be stored directly in a variable, so this is pretty convenient and usually makes sense.
But, as you've discovered, [u8] is not sized - it can only be held by a reference. Since process_ip_payloaddoes take it by reference, using ?Sized lifts the restriction that T is sized and allows [u8] to take its place. In general, it's usually a good idea to use ?Sized whenever you only deal with T behind indirection (like &, &mut, Box, Arc or other smart pointers).
It wasn't required because you didn't take [u8], you took in &mut [u8]. The critical thing is that &mut [u8] is a separate type from [u8]. The &mut here is part of the type, so T is literally &mut [u8].
In process_ip_payload, you take in &T. &mut [u8] is coerced/downgraded into &[u8]. Then since the parameter is &T, and the data it gets is &[u8], T is set to [u8].
T: Sized is required in both. It's just that &mut [u8] is Sized, while [u8] isn't.
There's a syntax difference here.
let mut buffer = ... means "let me change the value of buffer".
let buffer: &mut ... = ... means "let me change whatever the buffer points to".
If you did let mut buffer: &mut ... = ..., it would mean that you can both change what buffer points to and change the value at the thing it points to.
If you wanted to make buffer point to a different &mut [u8] value, then it needs to be declared let mut buffer. But if you are satisfied with changing the values in the [u8], and you don't need to point to a different slice, then let buffer = &mut ... works.
&[T] slices are dynamically sized. [u8; 34] is not a slice, it is an array, and it has a static size (34 bytes). &[u8] is a dynamically sized slice. &[u8; 34] is not a slice, it is a shared reference to an array with a static size.
If you want to transform a slice into an array reference, you can use some unsafe code, or a crate like arrayref which hides those details for you. You probably don't actually want to do this in general, but the option is on the table if you have a very strong case for it.
But I have a doubt about the coercion in this case.
&mut vec![0u8; 34]; is matched against &T. Doing eye pattern matchingT would have to be mut vec![_;34]. But there's no such thing as a mut T, only &mut T. So I guess T ends up being vec![0u8;34]. Well, vec is Sized and implements AsRef, so that's why it works here.
Looks like a good analysis! I agree with what you've said.
Responding to your individual question:
Ah, I might have misspoken. &mut [u8] is coerced to &[u8], not to [u8]. This is a general rule: rust downgrades &mut Xxxx into &Xxxx if it can't get &mut Xxxx to work otherwise. Since the pattern &T always starts with &, there's no way an &mut [u8] would fit. So it downgrades &mut [u8] to &[u8], and finds a pattern there.
This is accurate!
I would change it slightly, just what you call the types. vec![0u8; 34] produces a value with type Vec<u8>. Then &mut vec![0u8; 34] produces &mut Vec<u8>.
Similar to with the other one, &mut Anything won't fit &T, so Rust downgrades &mut Vec<u8> to &Vec<u8>. Then &Vec<u8> is matched against &T, and as you say, you get T = Vec<u8>.
Vec<u8> implements Sized and AsRef, so it works, like you say.
My words changed vec![0u8; 34] to Vec<u8>, but the content is the same!