Grokking the docs - vec[].join() not mentioned anywhere under Vec

Looking through some code I saw the use of the join method on a Vec:
let v = vec!["one", "two"].join("\n");

However, the docs on Vec make no mention of join. Nor do I see it anywhere in trait implementations under Vec. Digging a bit deeper, I think I found the implementation in std::slice::SliceConcatExt. But it's not referenced at all from the Vec doc.

Is there a recommended approach for navigating the std docs to understand what's available? Am I missing something? How would I know Vec has a join implementation?

Overall I think the Rust docs are outstanding in terms of providing details on the language. The API docs seem to take some time to fully understand.

2 Likes

Yep, you found it. There's a couple of factors at play here:

join is intended to be an inherent method of [T]. This is because joining is something that you should be able to do to any slice of strings, not just one that you own. (Vec itself has very very few inherent methods; mostly just things that deal with capacity, allocation, or that change its length)

Inherent methods of [T] can be used on Vec<T>, because Vec<T> implements Deref<Target = [T]>. They also show up in the documentation for Vec. Basically, what happens is that

vec.len()

is really sugar for

vec.deref().len()

and etc.

Unfortnately... for technical reasons unbeknownst to me[^1] , join could not be implemented as an inherent method of [T]. The standard library instead tries to make it as close to one as possible:

  • It is implemented as a trait on [T] (the SliceConcatExt trait you found), therefore the methods are available on [T] and Vec<T> (and anything else that derefs to [T]) whenever the trait is in scope.
  • The trait is always in scope because it is in the prelude. (only the standard library can cheat like this! :stuck_out_tongue: )

The end result of this is that it almost works exactly like an inherent method... but you can't see it in the Vec docs because the docs don't include trait impls for [T] (only inherent methods!).


Is there a recommended approach for navigating the std docs to understand what’s available? Am I missing something? How would I know Vec has a join implementation?

Don't be too discouraged; join and concat really are an unusual case! I don't think there's anything else in quite the same position as them.

That said: The easiest way to find these things right now is to:

  • Know what type your type Derefs to (and what that type Derefs to, and so on). Many standard lib types deref to a corresponding slice type, such as PathBuf -> Path.
  • Know what you want to do, and search by guessing its name. This can be a rough point if you don't know the name (and sometimes the search is picky about matching substrings).
  • Determine if those methods are available on any of the types that your type Derefs to.

Maybe one day when const generics are implemented, it might actually be possible to sift through the list of trait impls for [T] without getting bogged down by 50 impls of PartialEq for arrays of different sizes; this could be a more direct way to find a comprehensive list of methods available on a given type.


[^1] Maybe it has something to do with the fact that it is the only method on slices that is both overloaded and requires allocation? Contrast with other overloaded methods like get...

3 Likes

Excellent explanation! ...and you already answered a follow on question I was thinking of - "how does it resolve it". Thank you! Good to know that I only stumbled on a corner case :slight_smile:

Also - the Deref tip is very useful.

The technical reason is, [T] is defined in libcore but String is defined in liballoc. Internally libstd consists of a few subcrates, which can be used directly on nightly with #![no_std] flag. liballoc contains types and functions that requires heap allocation, like Vec, Rc and HashMap. And libcore contains the bare minimum requirements for the Rust language, like str, Iterator, Ord, and of course [T]. libcore is intended to be able to be used in environment even without OS, so you can use Rust for tiny embeded devices or even write a brand new OS entirely in Rust!

1 Like

A recent rustdoc PR might make some of these harder to find functions easier to find in future API docs:

1 Like

Right, but there's got to be more to it than that. slices also have a to_vec inherent method without needing any sort of crazy extension trait hack.

I suspect that there must be something cheaty going on that lets alloc and std add methods to these primitive types, but which for some reason specifically could not be used to implement concat.