#[inline] is all about cross-crate work; inside a crate, items not marked #[inline] may still be inlined by the optimiser, but it doesn’t do cross-crate inlining without #[inline] or LTO.
You don't need the #[inline] attribute when generics are involved, because Rust already has to cross-crate-export function metadata when generics are involved because otherwise it would be impossible to monomorphize. At that point, LLVM will inline the monomorphized functions as it deems fit.