Изменяемые ссылки

Последнее обновление: 28.05.2024

По умолчанию ссылки, как и переменные, нельзя изменять. То есть по умолчанию мы не можем изменить значение, на которое ссылается ссылка. Например:

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 и изменение ссылки в Rust

Чтобы значение по ссылке все таки можно было изменять, необходимо использовать изменяемую ссылку (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. Соответственно никаких конфликтов между ссылками не возникнет.

Также возникнет ошибка и в следующей ситуации"

fn main(){
	
	
	let mut s1 = "hello".to_string();
	let s2 = &mut s1;	// изменяемая ссылка
	let s3 = &s1;	// неизменяемая ссылка - ! Ошибка, так нельзя
	s2.push('!');
	println!("{}", s1);
	println!("{}", s3);
}

Здесь в одной области видимости создаются и используются изменяемая и неизменяемая ссылки, соответственно на этапе компиляции мы получим ошибку cannot borrow `s1` as immutable because it is also borrowed as mutable. Причем от этой ситуации следует отличать следующую:

fn main(){
	
	
	let mut s1 = "hello".to_string();
	let s3 = &s1;	// неизменяемая ссылка
	println!("{}", s3);	// hello
    // дальше неизменяемая ссылка s3 НЕ используется
	let s2 = &mut s1;	// изменяемая ссылка
	s2.push('!');
	println!("{}", s1);	// hello!
}

Здесь также в одной области видимости создаются две ссылки на один и тот же объект, причем одан из ссылок - неизменяемая. Однако этот пример нормально скомпилируется. Почему? Потому что неизменяемая ссылка создается и используется ДО создания изменяемой ссылки. После создания изменяемой ссылки неизменяемая ссылка НЕ используется. Соответственно здесь не возникнет конфликта между ссылками. Аналогичная ситуация будет и в следующем случае:

fn main(){
	
	
	let mut s1 = "hello".to_string();
    let s2 = &mut s1;	// изменяемая ссылка
	s2.push('!');
	println!("{}", s1);	// hello!

    // дальше изменяемая ссылка s2 НЕ используется
	let s3 = &s1;	// неизменяемая ссылка
	println!("{}", s3);	// hello!
}

Здесь наоборот сначала создается и используется изменяемая ссылка, а затем неизменяемая. НО опять же их создание и использование не смешивается, поэтому проблем не возникнет.

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850