Ручное — это когда мы сами должны выделять память и следить за ее очисткой. В таком подходе можно выделить 2 основных косяка — обращение к памяти по невалидному адресу и утечки. Мы получили откуда-то адрес в памяти и пытаемся обратиться по нему, даже не подозревая, что структура данных уже уничтожена и память недоступна. Также память может легко «утечь», если мы вдруг забыли про указатель и не освободили занятую память после завершения операции/функции/метода.
Автоматическое — это когда мы ответственны только за выделение памяти, а среда выполнения сама понимает какие объекты в памяти стали неактуальными и освобождает память. Такое реализовано во множестве современных языков, таких как Java, Go и т.д. Обращение к невалидной памяти тут практически невозможно. Основной недостаток такого подхода — наличие сборщика мусора, работа которого может повлиять на выполнение основной программы (событие stop the world в JVM).
В Rust реализовано нечто промежуточное. Память выделяем мы сами, но за ее корректным использованием следит компилятор, реализуя концепцию владения и заимствования. Если очень кратко, то вот этот код
let first = String::from("Hello");
let second = first;
println!("{}",first);не скомпилируется, потому что second теперь владеет ссылкой на участок памяти, где лежит строка и переменная first недействительна. Сама память освободится когда мы покинем область видимости переменной second (вышесказанное справедливо для объектов кучи). Но это только верхушка айсберга, внутри еще целое ведро связаных понятий, констркций и багов, поэтому осторожно, не повердите ненароком головной моск.
Сходу тяжело сделать что-то осмысленное, какое-то время нужно тупить в примеры и набивать руку. Пожалуй, в скором времени создам репо с неким референсом из связки Чистая архитектура и DDD, чтобы проверить кое-какие моменты и получить рабочий шаблон для последующей копипасты в проекты.