There's a huge semantic difference: a type with zero allowed values can never be constructed.
This means that a function that returns an uninhabited type is statically known to not return -- since there's no way it could construct an uninhabited value for a return expression. It also means you can coerce a value of uninhabited type into any other type, because any code which recieves a value of an uninhabited type must be unreachable.
For instance, in Rust you can write the following:
let weekday_name: &str = match weekday_number {
1 => "Sunday",
2 => "Monday",
3 => "Tuesday",
4 => "Wednesday",
5 => "Thursday",
6 => "Friday",
7 => "Saturday",
i => panic!("invalid weekday number: {i});
};
Because panic! aborts the program and doesn't return, its return type is uninhabited. Thus, a panic! can be used in a context where a string is expected.
We were talking about `void`, not `!`. It's clear that "never" type has a special language support. Difference between `void` and `()` is much less subtle.