You can do this if you want to own the iterated items:
fn main() {
let words = ["alpha", "beta", "gamma"];
let merged: String = words.iter().cloned().collect();
println!("{}", merged);
}
Alternatively, there is an owned array iterator API: IntoIter in std::array - Rust. (Or see this issue to see some more recent discussion of the status of this.)
So what actually happens on calling words.into_iter() is that method resolution will look for a type that implements IntoIterator (the trait that into_iter() belongs to). In particular, method resolution can do some auto-referencing, which means it will check [&str; 3] then &[&str; 3] and then &mut [&str; 3] for IntoIterator implementations. The first one that implements it is &[&str; 3] with an Item type &&str. (Generically &[T; N] turns into an iterator with item type &T). There is, as @skysch already mentioned, some newer work on providing an IntoIterator implementation for [T; N], too (already on nightly). This will eventually change the behavior of your words.into_iter() call to make the situation more similar to the Vec.
One reason why it took a while to get such an implementation is that it involces const generics (std::array::IntoIter has a N: usize parameter). Also since this change has the potential of breaking lots of existing code, there’s been work to special-case method resolution so that your words.into_iter() example might actually only change in the upcoming Rust 2021 edition.