По умолчанию ссылки, как и переменные, нельзя изменять. То есть по умолчанию мы не можем изменить значение, на которое ссылается ссылка. Например:
fn main(){ let message = "hello".to_string(); println!("До изменения: {}", message); change_message(&message); // !Ошибка - значение по ссылки нельзя изменять по умолчанию println!("После изменения: {}", message); } fn change_message(mes: &String){ mes.push('!'); }
Здесь для изменения значения по ссылке определена функция change_message()
. У структуры String есть метод push()
, который добавляет
к строке один символ. В данном случае мы пытаемся добавить к строке "hello" символ "!", то есть, чтобы в итоге получилась строка "hello!". Однако при компиляции
мы получим ошибку, поскольку по умолчанию значение по ссылке нельзя изменять:
Чтобы значение по ссылке все таки можно было изменять, необходимо использовать изменяемую ссылку (mutable reference), которая определяется с помощью оператора &mut:
fn main(){ let mut message = "hello".to_string(); println!("До изменения: {}", message); change_message(&mut message); println!("После изменения: {}", message); } fn change_message(mes: &mut String){ mes.push('!'); }
Здесь надо отметить три момента. Прежде всего, поскольку будет изменяться значение переменной, она должна быть определена с оператором mut:
let mut message = "hello".to_string();
Во-вторых, параметр-ссылка функции, через который надо изменять значение, определяется как изменяемая ссылка с &mut:
fn change_message(mes: &mut String){
В-третьих, при передаче ссылки в функцию значения перед ним также указывается оператор &mut:
change_message(&mut message);
В итоге, эта программа будет нормально компилироваться и одарит нас следующим консольным выводом:
До изменения: hello После изменения: hello!
Подобным образом можно определять переменные-изменяемые ссылки:
fn main(){ let mut s1 = "hello".to_string(); let s2 = &mut s1; s2.push('!'); println!("{}", s1); // hello! }
Следует отметить, что изменяемые ссылки имеют одно ограничение: в рамках одной области видимости может существовать только одна изменяемая ссылка. Например, следующий код не скомпилируется:
fn main(){ let mut s1 = "hello".to_string(); let s2 = &mut s1; // первая изменяемая ссылка let s3 = &mut s1; // вторая изменяемая ссылка s2.push('!'); println!("{}", s1); // hello! }
В данном случае в одной области видимости - функции main определены две изменяемые ссылки - s2 и s3. Однако мы можем использовать вложенные области видимости, которые располагаются до определения изменяемых ссылок во внешних областях видимости:
fn main(){ let mut s1 = "hello".to_string(); // let s2 = &mut s1; // если определить здесь, будет ошибка { let s3 = &mut s1; s3.push('?'); } let s2 = &mut s1; s2.push('!'); println!("{}", s1); // hello?! }
Одна переменная-изменяемая ссылка - s3 определена в анонимном блоке, который заканчивает свою работу ДО определения второй изменяемой ссылки - s2. Соответственно никаких конфликтов между ссылками не возникнет.