Note that this potentially reallocates twice, once for path1 and once for path2, whereas one could define an interface that can avoid it by taking multiple inputs and counting the total length:
returns_path_buf().into_join([path1, path2])
This could be useful because path manipulation often looks like joining {base_dir}/{subdir}/{filename} (e.g. in Cargo, {target_dir}/{profile_name}/{binary_target_name}).
Unfortunately that could cause name clashes with the Extend trait (or more accurately, the extend() method within it), if a decision were ever made to implement Extend for PathBuf.
I do think it'd be nice to improve upon the ergonomics of extend. Here's a few ideas I've previously sketched out (playground link). Note that the names of things are purely placeholders.
I prefer into_join because it makes it obvious that the only difference to join is that self is moved.
All append methods in std take &mut self.
concat can be misleading. It implies something like string concatenation. But joining with an absolute path replaces the root path completely.
That interface would be the equivalent of extend but taking ownership. I am proposing an equivalent of push but taking ownership. We can add both methods, but I think that the second one is more controversial. Something like this?
BTW, the current implementation of extend on PathBuf doesn't reserve capacity. It only calls push for every path because it takes an iterator, not a slice.
It is not always worth it to reserve capacity for all additional paths because the resulted path could be shorter in the case of something like .. or an absolute path which replaces the root.
Anyway, let's please open a new discussion for this if you want to have it. I would like to keep this thread about the equivalent of push for only one path.