Generalize Range<T> to Range<T, const IS_ITERATOR: bool = true>, and impl<T: Copy> Copy for Range<T, false>, impl<T: Step> Iterator for Range<T, true>, and impl<T: Step> IntoIterator for Range<T, false>. "All" this needs IIUC is an {integer}-like fallback to true for IS_ITERATOR if unbound, and then a..b can be Range::<_, _>::new(a, b) rather than Range::<_>::new.
Though I think this ties very strongly into "defaults impact inference?" Though it's somewhat plausible that Range could get a targeted magic fix before we have inference-impacting defaults. It's also unfortunate that we'll have to deal with Range<T, false> all over the place, but it's better than custom not-std::ops::Range types just to get Copy.
It is true that Range<T, true> for T: !Step is !Iterator. There's no good reason to forbid creating such a range (just like how we don't forbid creating ranges of incomparable types today), so the "more accurate" C++-style template specialization naming would be ENABLE_ITERATOR.
In an alternative universe we could have Range be Copy, with a method .into_iter() that creates a RangeIter, and everything would be fine.
Unfortunately the current Range is already what we wanted RangeIter to be, so it's kind of a misnomer. But nothing prevents creating a new type for non-iter ranges (maybe call it Interval or something), and then the iterator over intervals would be called ranges due to historical issues.
Then, on a new edition (maybe Rust 2028 or something), make a .. b desugar to Interval rather than Range, and do a rustfix pass that checks if every usage of a .. b on old code works ok as Interval, and if not, replace it by an explicit call to Range::new.
Among other things, for i in a .. b would continue to work fine because Interval would impl IntoIterator with IntoIter being Range
I think that this would be better than having Range be generic over whether it is or is not an iterator. Every other Rust type I know of has iterators defined as another struct.