Confused about slice syntax

I'm trying to use a slice two ways. First by calling a function that takes a slice. Second by calling .into() on a slice to create a Cow.

I'm confused because when calling the function the slice syntax works. But when trying to create the Cow the same slice syntax is rejected. I've figured that I can instead use (&[0, 1, 2][..]).into() and the Cow case will work, but I'm confused why I need to do that.

Thanks!

use std::borrow::Cow;

fn test() {
    test_fn(&[0, 1, 2]);
    let cow: Cow<[u8]> = (&[0, 1, 2]).into();
}

fn test_fn(items: &[u8]) {
}

Function takes your slice as-is, no type coercion required. But your let binding requires conversion -- on the left side you have a name, that is of a Cow<_> type and on the right side you have a slice, since rust doesn't implicit type conversions you must do it explicitly. Thankfully T::from and v.into() do the job well.

Thanks for your help.

Ok, I follow that.

I think I follow this too... and I think in the above code that's what I'm trying to do. I'm calling .into() on the slice hoping to get a Cow. But when I do that:

let cow: Cow<[u8]> = (&[0, 1, 2]).into();

I get compiler errors. Only when I add extra [..] does the compiler accept:

let cow: Cow<[u8]> =  (&[0, 1, 2][..]).into();

That's where I'm confused, why do I need to add the extra [..] in the .into() case, but not in the function call case.

When calling the function the compiler sees &[u8; 3] and knows that it needs &[u8]. It can figure out that it should coerce the slice from this information.

When creating the cow, the compiler tries to look for an Into<Cow<[u8]>> impl for the type &[u8; 3] but finds none. This is because the impl is on the type &[u8] instead.

Basically it fails because it has to do two conversions to reach the target, which it isn't clever enough to do.

4 Likes

&[0, 1, 2] is ambiguous. It can either mean &[u8] (slice) or &[u8; 3] (fixed-length array). The type is deduced from context or coerced to the right type. It's similar to how 1 is ambiguous, because it can be u8 or i32 or other integer type.

For example:

[&[0, 1, 2][..], &[0, 1, 2], &[0, 1, 2]]

is an array of slices. The first one establishes the type, the rest is implicit.

Coercions and type inference generally work when there's direct unambiguous way to deduce the type. In case of (&[0, 1, 2]).into(); there isn't, because the compiler has to know the type on the left to find .into() to call, so it has to decide on the type of the slice/array first.

2 Likes

Thanks to everyone... I though that &[T] was always a slice, didn't realize that there was a difference between slice and reference to array. Thanks again!

Oh, I wasn't reading carefully and somehow skipped the [..] part. I'm actually a bit wrong about saying there are no coercions in rust. For instance [T; n] is implicitly coerced to [T] ([https://doc.rust-lang.org/reference/type-coercions.html](http://type coercions in rust reference)). So the type of &[0,1,2] should be &[u8, 3] that's not a slice itself it's a borrow of array, while the slice borrow would be of type &[u8]. I guess the compiler isn't smart enough to try to coerce array ref to slice ref before dealing with .into() call. Try the Cow::from(&[0,1,2])

1 Like

Thanks for the coercion links.

That still seems to have the same issue:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d15d2e499185ea0f0277cc55191278ff

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.