This is… interesting. I imagine that we could extend the slice type [T] to include the stride as part of its type (let’s use Stride<T, const n: usize> as a strawman). The neat thing is that &Stride<T, n> as the same layout as &[T]. The only difference now is that an access is a pointer offset by n (known at compile-time), instead of by size_of::<T>(). This is just as fast as a normal slice access, and [T] becomes an alias for Stride<T, size_of::<T>()>. Of course, we’d need to require that n be at least as large as the size of T.
In your example, we could coerce &[String] into a &Stride<&str, { 3 * size_of::<usize>() }>. Of course, to take full advantage of this we’d need const generics, so the most general type you accept is
fn foo<T, const n: usize>(stride: &Stride<T, n>) { .. }
// imagine we add `_` as sugar for the "minimally constrained parameter"
// so that we can write this somewhat less painfully:
fn foo<T>(stride: &Stride<T, _>) { .. }
Even neater, with const generics you can define this type entirely in a crate!