How to pass compound data to a function?

Another title to my question could read "Stack and heap – a beginners approach". I’ve a function that needs to read through an array of Strings. The parameter’s type could be

  1. [String]
  2. &[String]
  3. &[&str]

From my understanding variant three &[&str] is best way to go with minimal stack-consumption for there are solely pointers stored on stack which will be the least memory-consumption.

  1. Am I right?
  2. Can I make the same assumption about runtime, for this runtime is partly a result of the copy-operations?

If you have an array or vector of Strings and want your function to read its contents but not consume it, &[String] would be the best option. &[&str] would require you to create a new array or vector with references to the elements of the already existing one.

If you want to make the caller’s life easy, and avoid extra allocations in all cases, accept a &[impl AsRef<str>]

(Also, the parameter type cannot be your #1, [String] or [&str], because those are dynamically sized types that can’t be passed by copy.)

This doesn't affect the stack at all.

[T] is not a valid parameter, and it's mostly unusable as a type, because it has no length. The length of slices is stored in the "fat" reference (in the & part).

&[T] parameter is passed and used exactly the same for all types of T.

In most cases parameters are passed in registers, not even via the stack. The contents of a borrowed slice will never be copied when passed around. A slice argument is just pointer and a length, literally two integers, so it's cheap to pass around.


There is a difference between &str and String, but it's not directly related to the stack. It's true that String stores its data on the heap, but that doesn't mean that non-String types would use the stack.

&str is completely agnostic about where its data comes from. You can't directly allocate &str anywhere. It's not a string! It's a temporary permission to view some other string type. That other thing it is borrowing from could be a String, or could be other data stored anywhere. It could come from an on-stack ArrayString, or could be hardcoded into the executable, or could be a sub-slice of a completely different string type.

In practical terms:

  • &[String] forces the caller to have String allocated, so the caller has to make heap-based Strings even if they use a different string type.

  • &[&str] is physically different in memory than &[String], so if the caller has &[String], they will now need to allocate Vec<&str> to borrow all the strings, and lend you the Vec as &[&str].

None are ideal. If it's in internals of your own program, just use the type that fits what the callers of your function already have.

If it's a public API and you want to offer flexibility:

  • &[impl AsRef<str>] is relatively simple to use. It will generate a separate copy of your function for every string-like type it's used with.

  • impl Iterator<Item = &str> is most flexible, since it allows providing strings from any data source without allocating any particular type.

7 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.