Уроки · 46
Владение и ссылки
За несколько последних уроков for встречался в трёх разных видах:
&points в уроке «Список кружков», &mut bullets в
уроке «Турель стреляет очередью» и просто
circles, вовсе без &, в уроке «Кружки
исчезают». Каждый раз разница объяснялась на
месте, по ходу дела. Теперь, когда все три уже знакомы, стоит собрать
их вместе и назвать, что происходит на самом деле.
Что такое ссылка
Заголовок раздела «Что такое ссылка»Любое значение в программе — например, список Vec — где-то лежит в
памяти компьютера, пока программа работает. Список может быть
огромным, на тысячи элементов, но ссылка на него — это не сам
список, а что-то вроде бумажки с адресом: «он лежит вон там». Ссылка
всегда занимает маленькое фиксированное место, независимо от того,
насколько велико то, на что она указывает, — ведь в ней записан
только адрес, а не содержимое целиком.
Символ & перед значением и означает «дай ссылку на это, а не само
значение». В описаниях типов иногда пишут &T — это не что-то
конкретное, а общая запись: T здесь просто заглушка вместо
настоящего типа («T» от английского type), значит «ссылка на что
угодно» — может быть &Vec<Bullet>, может быть &f32, какой тип
подставишь, такая и ссылка.
Ссылка для чтения
Заголовок раздела «Ссылка для чтения»for &(x, y) in &points { draw_circle(x, y, 20.0, YELLOW);}&points — ссылка на список points, только для чтения: каждая точка
читается по одной, чтобы нарисовать кружок, но ничего не меняется и
не забирается в собственность. После цикла points остаётся ровно
тем же списком, каким был, и им можно пользоваться дальше как ни в
чём не бывало.
Ссылка для изменения
Заголовок раздела «Ссылка для изменения»for bullet in &mut bullets { bullet.x += bullet.direction_x * SPEED; bullet.y += bullet.direction_y * SPEED; draw_circle(bullet.x, bullet.y, 8.0, YELLOW);}&mut bullets — тоже ссылка, тоже не забирает список в собственность,
но, в отличие от &bullets, разрешает менять то, на что она
указывает: bullet.x и bullet.y можно записывать заново прямо
внутри цикла. Сам список bullets при этом остаётся тем же самым
списком — его длина не меняется, просто значения внутри
перезаписываются.
Владение и перемещение
Заголовок раздела «Владение и перемещение»for circle in circles { // ... kept_circles.push(circle);}Здесь & нет вовсе. circles не одалживается на время — список
забирается в собственность целиком, а каждый его элемент по
очереди становится собственностью переменной circle внутри цикла.
Это называется владением: у каждого значения в Rust есть ровно
один владелец, и когда владение передаётся дальше (как здесь, из
circles в circle), прежний владелец больше не может им
пользоваться — переменная circles после такого цикла попросту
перестаёт существовать.
Вот почему после этого цикла нужно заново создавать circles:
circles = kept_circles;Старого circles уже нет — есть только то новое значение, которое мы
только что туда положили.
Что здесь происходит
Заголовок раздела «Что здесь происходит»- Значение в памяти всегда лежит у кого-то одного — это и называется владением. Владение можно передать дальше (переместить), и тогда прежний владелец перестаёт быть действителен.
- Ссылка (
&) — способ обратиться к значению, не забирая владение: временный «адрес», а не сама вещь. Ссылка занимает маленькое, фиксированное место, что бы она ни описывала. &points— ссылка только для чтения: значение видно, но менять его через такую ссылку нельзя.&mut bullets— ссылка для изменения: значение можно менять через неё, но владение всё равно остаётся у исходной переменной.for circle in circlesбез&— не ссылка вовсе, а перемещение владения: список тратится целиком, элемент за элементом, и переменнойcirclesпосле цикла больше нет.