When does one use slices as arguments?

Looking at this piece of code, I see functions accepting slices of structs.

pub fn calc_shortcuts(
    node: NodeId,
    dijkstra: &mut dijkstra::Dijkstra,
    edges: &[Way],
    up_offset: &[EdgeId],
    down_offset: &[EdgeId],
...
  1. I thought that I've never used this in Rust before. I guess I use too much copying to pass vectors. When should slices be used?

  2. If I want to select non-contiguous items of vec, will slice help, or not?

  3. Is a slice as a struct member a good idea?

  1. In general, you should pass a slice whenever you don't need to accept a more specific container. If you don't need to take ownership of elements or to grow the container, then there's no reason to pass a Vec by value or by mutable reference. (And there's no reason to ever pass a Vec by immutable reference.)
  2. No. Slices are contiguous.
  3. It depends on what you want to do.
3 Likes

Also, slices are often more specific than you actually need. If you don't need random access, it's often better to take impl IntoIterator<Item=...> instead.

5 Likes

As with anything borrowed - more often no then yes. References are temporary, but structs are usually thought of as something long-lasting, therefore owning everything it contains. For view-like structs, which are temporary themselves, that's probably a good idea, but whether this view-like struct is a good idea by itself - depends on the use-case.

4 Likes

The owned variation of a slice is a Box<[T]> boxed slice, it'd be much likely (although still relatively uncommon compared to Vec) if you don't need to resize since a Box<[T]> doesn't need to store the capacity which gives you a smaller struct.

Box also implements FromIterator, so you can collect to it as well..

If you pass a varying number of items to a function, you should almost always use slices.

Never use &Vec as an argument, as it's as limited as a slice, but less efficient and less flexible.

Use &mut Vec only if the function needs to change number of items in the vector (e.g. append to it). &mut [] is fine for modifying existing contents.

The exceptions are:

  • if the method is going to keep the elements (e.g. it's a fn new() or a setter), then you can take Vec or impl Into<Vec> as an argument.

  • if the number of items can be quite large, and the function is able to process them incrementally one by one without keeping them, then you can take an iterator to support processing with a lower memory footprint.

4 Likes

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.