# ダメ
str = "abc\tdefg\th"
1 while(str.sub!(/\t+/, ' ' * ($&.size * 8 - $`.size % 8)))
p str

# イイ
str = "abc\tdefg\th"
str =~ /\t+/
1 while(str.sub!(/\t+/, ' ' * ($&.size * 8 - $`.size % 8)))
p str

subの第一引数の正規表現より前で行われた正規表現の結果が
subの第二引数で用いた$&や$`に入るんですね。
仕様として捉えるべきなのでしょうか。
タブを空白に置き換えようとしました。