[ 1, 2, 3 ][ .. ] invalid type?

[ 1, 2, 3 ][ .. ] -- what type is it? Such type is not valid in Rust. Is that correct?

But if so, why I can implement trait for that?

impl < T > Trait1 for [T] { }

The type is [i32]. It's not an invalid type, but it is unsized, which means that it can only exist behind a pointer-like type such as a reference or Box.

4 Likes

How?

Box::new( [ 1, 2, 3 ][ .. ] )

What is wrong with that?

Thanks Alice.

Well, what you wrote here doesn't work because to call Box::new, you have to first create the value when it is not behind a pointer-like type, then pass it as an argument.

One way to create a Box<[T]> is via the Vec::into_boxed_slice method. For example, vec![1,2,3].into_boxed_slice() would create an Box<[i32]>.

2 Likes

You can create an array behind a pointer, too, and turn it into a slice. E.g.

Box::new([1, 2, 3]) as Box<[i32]>

or

&[1, 2, 3] as &[i32]

For the latter, &[1, 2, 3][..] should work as well.

4 Likes

Thanks, but this &[i32] is slice, not [i32] and vec![ 1, 2, 3 ].into_boxed_slice() is alloc::boxed::Box<[i32]> neither [i32].

How to get [i32]? Is that possible?

No, it can only exist behind a pointer-like type such as a reference or Box.

3 Likes

Does it mean such implementation impl < T > Trait1 for [T] { } can never be used despite it is possible to define it?

No, such an impl can certainly be used. For example, a method might accept an &T where T implements a trait, and then an unsized type would be fair game.

4 Likes

Is any difference in layout between

Box::new([ 1, 2, 3 ]) as Box<[i32]>

and

&[ 1, 2, 3 ][ .. ]

As I understand only difference is segment, in the first case it is heap, but in the second it is either stack or data segment?

How to get std::rc::Rc< [ i32 ] >?

A note on teminology:

  • [T] is a slice
  • &[T] is a reference to a slice or a "shared slice"...
    • But since it has to be behind a pointer, and "reference to a slice" is a mouthful, it's also commonly just called a slice
      • Including in the standard documentation, etc.

So you have to be a bit careful when the distinction matters.


You've probably already worked with slices quite a bit. str is, under the hood, a [u8]. As with slices and any other dynamically sized type, you can't "get" a bare str. This is why you work with &str all the time.

Here are some trait implementations for slices. For example, all the methods of Ord take Self types behind references, so there is no problem implementing it for slices:

impl<T: Ord> Ord for [T] {
    fn cmp(&self, other: &[T]) -> Ordering {
        SliceOrd::compare(self, other)
    }
}

And there are a bunch of implementations for str as well.


Since you're asking about an indexing expression in particular:

  • [1, 2, 3] is an array, [i32; 3]
  • [1, 2, 3][..] performs an Index operation using a Range
    • As the documentation says, this is sugar for *[1, 2, 3].index(..)
    • And index takes &self and returns &self::Output, which in this case is &[i32]
    • Then the * dereference applies and you have a [i32] -- a bare slice
  • So [1, 2, 3][..] is a [i32]

What happens then depends on what you try to do with it. In particular, this is a place expression. Because it is dynamically sized, if you try to move it:

let x = [1, 2, 3][..];

That will be an error. But if you leave it "in place", you can still do things with it:

let len = [1, 2, 3][..].len();

This is analogous to how I can do this:

struct Data { s: String }
fn f(data: Data) -> Data {
    // `s` doesn't move
    let len = data.s.len();
    println!("{}", len);
    data
}

without moving s out of data. (If I said let my_s = data.s;, I would move s and wouldn't be able to return data.)

5 Likes

Super clear, thank you @quinedot! I didn't know [ T ] is slice, I thought &[ T ] is slice.

Maybe you know answers on the those 2 questions above?

1 Like

Slices, arrays, (the slices in) boxed slices, and (the data behind the pointer in) Vecs all have the same layout.

You can perform unsized coercion to get an Rc<[T]>:

fn main() {
    let rc: Rc<[i32; 0]> = Rc::new([]);
    let rc: Rc<[i32]> = rc;
}

Or use this From implementation from Vec, or a variety of other cloning or converting approaches.

2 Likes

I used into + explicit type. But maybe there is a way to get Rc<[i32]> without explicitly defining type and using from/into? It seems there is no such option?

Unsized coercion doesn't use from/into. You can do it in one step; my first example was just to better illustrate the types.

let rc: Rc<[i32]> = Rc::new([1, 2, 3]);

There will probably have to be an explicit type (or trait bound that can only be satisfied by the unsized version) somewhere, or Rust will just assume you meant the concrete sized type that you constructed. If I didn't put a type on this example, and if nothing else demanded it (e.g. return value type), I would have gotten an Rc<[i32; 3]>.

3 Likes

Thank you all for help :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.