UTF-32 appears to simplify things a lot, but in practice O(1) indexing of code points and simple iteration over code points is not actually all that useful, because code points are not characters.
If you to access code points, then UTF-32 gives you a nice fixed-width (albeit quite large) encoding to work with. Some applications or libraries may use UTF-32 internally to speed things up.
If you deal with things on screen or what humans perceive as character (graphemes) then there is nothing really that can help you at the encoding level because graphemes can be arbitrarily long with regard to code points: e̬̱ͤ̈́̿̂̅́͟x̰̞̻̼̻̼͉̰ͫͧ̑͂͊͜ͅa̶̳͖͔̞̰͈̯͓͆̓̎̉̏̎̚m̛̮̩͙ͦ͗͆̄̋́̄p͎̥̠͈̮͚̾ͅl̛̗̈̄e̴̞̫͖̝̘̋̑̄̎ͥͬ̾ (this probably doesn't even render properly).
Sadly, most APIs use either UTF-8 or UTF-16 and consider code units the most important entity, not code points. And that's inexcusable and frankly a mess, because I can count the number of times I needed to access a code unit instead of a code point on zero hands.
> e̬̱ͤ̈́̿̂̅́͟x̰̞̻̼̻̼͉̰ͫͧ̑͂͊͜ͅa̶̳͖͔̞̰͈̯͓͆̓̎̉̏̎̚m̛̮̩͙ͦ͗͆̄̋́̄p͎̥̠͈̮͚̾ͅl̛̗̈̄e̴̞̫͖̝̘̋̑̄̎ͥͬ̾
As how many characters am I supposed to perceive this, anyway? Some of those tiny squiggles are in fact letters (I mean they look like letters, I see an e,m,u,o,i and r in there). What if you'd make a crossword this way?
a = s.indexOf("e̬̱ͤ̈́̿̂̅́͟")
b = s.indexOf("m̛̮̩͙ͦ͗͆̄̋́̄")
s[a:b]
This operation is fast with code unit indices and slow with code point indices.
Code Unit. The minimal bit combination that can represent a unit of encoded text for processing or interchange. The Unicode Standard uses 8-bit code units in the UTF-8 encoding form, 16-bit code units in the UTF-16 encoding form, and 32-bit code units in the UTF-32 encoding form.
Code Point. (1) Any value in the Unicode codespace; that is, the range of integers from 0 to 10FFFF16. (See definition D10 in Section 3.4, Characters and Encoding.) Not all code points are assigned to encoded characters. See code point type. (2) A value, or position, for a character, in any coded character set.
Unicode is a standard, that maps the giant number of letters to unique numbers.
UTF8 (and UTF16, UTF32) are various encodings of that. Unicode itself is not an encoding; unicode just says "letter 'č' is 269"; UTF-8 says "We will represent it as two bytes, first is C4, second is 8D".
UTF8 is indeed variable width; UTF16 and UTF32 are, AFAIK, fixed width. Both use unicode standard.
UTF-8 is variable width using 8, 16, 24, or 32 bits per code point.
UCS-2 is fixed width using 16 bits per code point.
UTF-16 is variable width using 16 or 32 bits per code point. It's an extension of the fixed width UCS-2 format: every valid 16-bit UCS-2 code point is also a valid UTF-16 code point, but UTF-16 also includes 32-bit code points.
UTF-32 is fixed width using 32 bits per code point.
I wondered why you didn't write that "UTF-8 is variable width using 8, 16, 24, 32, 40, or 48 bits per code point", because that's how Prosser/Thompson/Pike's UTF-8 idea was proposed, as shown in the "most elegant hack" article.
And here's the answer from Wikipedia: In November 2003 UTF-8 was restricted by RFC 3629 to end at U+10FFFF, in order to match the constraints of the UTF-16 character encoding. This removed all 5- and 6-byte sequences, and about half of the 4-byte sequences.
Even though they eliminated the 5th and 6th bytes, UTF-8 can still represent all of Unicode's 1,114,112 code points.
I'm mentioning this because others probably are wondering the same thing.
Your knowledge is wrong.
As of 2012, there are 110,182 assigned Unicode characters, and 137,468 more which have been reserved for private use.
With 16 bits there are only 65,536 possible values, and so UTF-16 has to be variable width. (Though it was originally intended to be fixed width. A misconception unfortunately embedded in the design of languages from the time such as Java.)
Outside of ANSI terminals, teletypes and and dot matrix printers, they really don't work like that any more. If they did you'd be able to substitute LFCR for CRLF, or use multiple LFs with a single CR to leave multiline gaps - and multiple CRs would be redundant. Try sending a CSV file with LFCR line-endings, or HTTP headers separated by CRCRCRCRLF... see if that works everywhere reliably. Also, CRLF would be an acceptable substitute everywhere for UNIX LF line-endings, but clearly it's not. Fact is, CRLF is a two-byte magic number in a lot of places.
$ man terminfo | grep cursor_down | head -1
cursor_down cud1 do down one line
$ tput cud1 | hexdump -C | cut -f 1-20 | head -1
00000000 0a |.|
$ man terminfo | grep carriage_return | head -1
carriage_return cr cr carriage return (P*)
$ tput cr | hexdump -C | cut -f 1-20 | head -1
00000000 0d |.|
If you ever write a bash script that does more advanced things with the terminal, you'll get familiar with tput, termcap and terminfo pretty quickly.