Changing the double-quotes to $'...' (single quotes preceded by a $) could rescue for the variable $test:
test=$'a\tb\tc'
Here is the zsh manual for QUOTING (double-quoting and $'...'):
QUOTING
...
A string enclosed between $' and ' is processed the same way as the string arguments of the print builtin
...
Inside double quotes (""), parameter and command substitution occur, and \ quotes the characters \, `, ", $, and the first character of $histchars (default !).
--- zshmisc(1), QUOTING
For example:
"\$" -> $, "\!" -> ! etc.
"\t" -> \t (zsh does not recognize as tab this case), "\a" -> \a etc.
It does not treat the escape sequence \t as tab when it is used inside double quotes, so "a\tb\tc" does not mean "atabbtabc". (But things are a little more complicated: builtin echo recognizes the escape sequence \t.)
(1) ... IFS=$'\t' read -r ... (the original form)
Because expanding "$test" dose not have any tab characters, so read assigns the whole line to $user:
user: a b c
host:
key:
(But echo recognizes the escape sequence \t as the tab.)
(2) ... IFS='\t' read -r ...
Again, expanding "$test" does not have any tab characters, so read separate the field by \ and t according $IFS.
a\t\b\tc splits into a (to $user), \(separator), `` (empty to $host), t(separator), and the rest of the line (b\tc to $key):
user: a
host:
key: b c
(But again, echo recognizes the escape sequence \t as the tab.)
Here is the code changed from test="..." to test=$'...':
function test() {
test=$'a\tb\tc'
while IFS=$'\t' read -r user host key; do
echo "user: $user"
echo "host: $host"
echo "key: $key"
done <<< "$test"
}
test
The output is:
user: a
host: b
key: c
PS: it is worth reading POSIX's Quoting specification, which is simpler than zsh's (https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02)