Это правда, сборка мусора на самом деле не требуется для оптимизации хвостового вызова.
Однако, допустим, у вас есть 1 ГБ ОЗУ и вы хотите отфильтровать список целых 900 МБ, чтобы оставить только положительные. Предположим, около половины положительные, половина отрицательные.
На языке с GC вы просто пишете функцию. GC будет происходить несколько раз, и вы получите список 450 МБ. Код будет выглядеть так:
list *result = filter(make900MBlist(), funcptr);
make900MBlist будет постепенно увеличиваться до GCd, так как фильтр деталей больше не упоминается.
На языке без GC, чтобы сохранить хвостовую рекурсию, вы должны сделать что-то вроде этого:
list *srclist = make900MBlist();
list *result = filter(srclist, funcptr);
freelist(srclist);
Это приведет к необходимости использовать 900 МБ + 450 МБ, прежде чем окончательно освободить srclist, поэтому программе не хватит памяти и произойдет сбой.
Если вы напишите свой собственный filter_reclaim, это освободит список ввода, так как он больше не нужен:
list *result = filter_reclaim(make900MBlist(), funcptr);
Он больше не будет хвост-рекурсивным, и вы, вероятно, переполните свой стек.