На самом деле это довольно просто: причина, по которой $SAFE
ведет себя не так, как вы ожидаете от глобальной переменной, заключается в том, что она не является глобальной переменной. Это волшебная штука единорога.
В Ruby немало таких волшебных штуковинных штук единорогов, и они, к сожалению, не очень хорошо документированы (вообще не документированы), так как разработчики альтернативных реализаций Ruby выяснили сложный путь. Эти штуковины ведут себя по-разному и (по-видимому) непоследовательно, и почти единственное, что у них общего, - это то, что они выглядят как глобальные переменные, но не ведут себя как они.
Некоторые имеют локальный охват. У некоторых есть локальная область потока. Некоторые волшебным образом меняются, и никто их не назначает. Некоторые имеют магическое значение для переводчика и меняют поведение языка. У некоторых есть иная странная семантика.
$SAFE
имеет почти все вышеперечисленное: локально для потока, что означает, что если вы измените его в одном потоке, это не повлияет на другие потоки. Он локальный, то есть если вы измените его в локальной области (например, в классе, модуле, методе или блоке), он не повлияет на внешнюю область (как вы обнаружили). Это имеет магическое значение для интерпретатора, поскольку установка значения, отличного от 0
, приводит к тому, что некоторые вещи не работают. И у него есть дополнительная странная семантика в том, что вы можете только увеличить его значение, никогда уменьшить it.