Array or Vector of References?

I was working on a practice problem. In short, I want to be able to have an array or vector of &str preallocated and later place each &str to a cell in the array or vector. I ended up doing this. But without going into the links, here is the gist of what I want to do:

  1. Have an array or vector of &T and each cell is initialized into a dummy &T, for example, &"".
  2. I can do array[index] = x (x's type is &T). Or it's vector version: vec[index] = y (y's type is &T).

I was able to achieve this with vector (although really ugly) but not with array.

let y = &"some str";
let mut place: Vec<&str> = Vec::new();
place.push(&"");
place.push(&"");
place[0] = y;

This need seems to be pretty unidiomatic, is there an idiomatic way of doing this for array and vector?

Do you mean this?

fn main() {
    let y = "some str";
    let mut place: Vec<&str> = vec![""; 2];
    place[0] = y;
    
    println!("{:?}", place);
}
["some str", ""]

Note that "some str" is already of type &str, so adding an extra ampersand makes it into an &&str. The extra reference is then auto-removed by the compiler again.

2 Likes

Consider using an array or vector of Option<&T> and using None as your sentinel value. Due to niche optimizations, it'll use the same amount of space, and there's no problem if something wants to use the empty string as a real value.

2 Likes

The idiomatic version for an array of &str is just a slight variation on @alice's suggestion.

    let y = "some str";
    let mut place = [""; 2];
    place[0] = y;

This won't work for a fully generic &T instead of &str without some sort of default value, which is where @2e71828's suggestion comes in (on top of being able to distinguish unassigned values from the default value). Moreover, if the default value isn't const, you'll have to store those values somewhere in order to refer to them anyway.


On the confusion between str, &str, and &&str: A str is a collection of UTF8 bytes with no static size. In trait terms, it does not implement Sized. For this reason, it generally has to reside behind a (wide) pointer of some sort -- like &str. &str is a pointer to the bytes and a count of the number of bytes.

As Alice said, a literal string like "" is a &str already; it can be promoted, so you can always consider it a &'static str.

If you go the generic T route, you will probably need to add a bound like where T: ?Sized if you want to handle &str. The reason is that most places in Rust have an implicit Sized bound, so that when you take or return generic things by value, you don't have to add the Sized bound yourself. And, as mentioned, str doesn't implement Sized. The ?Sized bound removes the implicit Sized bound.

5 Likes

Thank you for the help and explanation on &str. I misread the array/vector documentation - I incorrectly assume you need to put the type to initialize it, like:
Wrong: let mut place = [&str, 2] but it really should be let mut place = ["", 2]. I made the same mistake for the vector case, so I ended up with the ugly way of initializing the vector.

I think both @alice and yours should be the solution as one talks about the issue with vector, the other focuses on array. But it seems I can only set one so I will set yours as it has a pointer to alice's answer.

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.