В Rust представлен код: fn borrow_str() -> &String { let s = String::from(\"hi\"); &s }\nПочему он не компилируется; объясните модель владения и заимствований и предложите несколько способов исправить функцию
Проблема: функция возвращает ссылку на локальную переменную. Код ```rust fn borrow_str() -> &String { let s = String::from("hi"); &s } ``` не компилируется, потому что `s` удалится (drop) при выходе из функции, а возвращаемая ссылка станет висячей. Компилятор сообщает примерно: “returns a reference to data owned by the current function”. Коротко о модели владения и заимствований: - Каждый ресурс (например, `String`) имеет одного владельца. Когда владелец выходит из области видимости — ресурс освобождается. - Ссылка `&T` или `&mut T` заимствует ресурс у владельца; сама ссылка не продлевает время жизни значения. - За lifetimes (времена жизни ссылок) следит borrow checker: нельзя вернуть ссылку, чей lifetime короче, чем ожидаемый lifetime возвращаемого значения. - Чтобы вернуть ссылку, источник должен жить дольше вызывающего (например, быть передан в функцию или иметь `'static` lifetime). Варианты исправления (с пояснениями): 1) Вернуть сам `String` (передать владение): ```rust fn borrow_str() -> String { let s = String::from("hi"); s } ``` Плюс: просто и безопасно — вызывающий получает владение. 2) Вернуть строковый литерал с `'static` lifetime: ```rust fn borrow_str() -> &'static str { "hi" } ``` Строковый литерал живёт всё время выполнения программы, поэтому ссылка безопасна. 3) Вернуть ссылку, но принимать источник извне (аннотировать lifetime): ```rust fn borrow_str(s: &'a String) -> &'a String { s } // или более обобщённо fn borrow_str_str(s: &'a str) -> &'a str { s } ``` Здесь функция не создаёт значение, а лишь возвращает ссылку на переданный объект; borrow checker проверит, что источник живёт достаточно долго. 4) Вернуть умный указатель (разделяемое владение): ```rust use std::rc::Rc; fn borrow_str() -> Rc { Rc::new(String::from("hi")) } // или для многопоточности: std::sync::Arc ``` Выдаёт владельца-обёртку, которую можно клонировать для совместного владения. 5) Вернуть Box (передать владение на куче): ```rust fn borrow_str() -> Box { Box::new(String::from("hi")) } ``` Box тоже передаёт владение, но значение хранится на куче. 6) (Хак) Утечка в `'static` — небезопасно в общем случае, но работает: ```rust fn borrow_str() -> &'static String { let s = Box::leak(Box::new(String::from("hi"))); s } ``` Это делает `String` пожизненным (утечёт память) — использовать с осторожностью. Вывод: нельзя возвращать ссылку на локальную переменную. Выберите подходящий паттерн: возвращать владение (`String`, `Box`, `Rc/Arc`) или ссылку на внешний ресурс (передать его в функцию и использовать lifetime-параметр), либо использовать литерал с `'static`.
```rust
fn borrow_str() -> &String {
let s = String::from("hi");
&s
}
```
не компилируется, потому что `s` удалится (drop) при выходе из функции, а возвращаемая ссылка станет висячей. Компилятор сообщает примерно: “returns a reference to data owned by the current function”.
Коротко о модели владения и заимствований:
- Каждый ресурс (например, `String`) имеет одного владельца. Когда владелец выходит из области видимости — ресурс освобождается.
- Ссылка `&T` или `&mut T` заимствует ресурс у владельца; сама ссылка не продлевает время жизни значения.
- За lifetimes (времена жизни ссылок) следит borrow checker: нельзя вернуть ссылку, чей lifetime короче, чем ожидаемый lifetime возвращаемого значения.
- Чтобы вернуть ссылку, источник должен жить дольше вызывающего (например, быть передан в функцию или иметь `'static` lifetime).
Варианты исправления (с пояснениями):
1) Вернуть сам `String` (передать владение):
```rust
fn borrow_str() -> String {
let s = String::from("hi");
s
}
```
Плюс: просто и безопасно — вызывающий получает владение.
2) Вернуть строковый литерал с `'static` lifetime:
```rust
fn borrow_str() -> &'static str {
"hi"
}
```
Строковый литерал живёт всё время выполнения программы, поэтому ссылка безопасна.
3) Вернуть ссылку, но принимать источник извне (аннотировать lifetime):
```rust
fn borrow_str(s: &'a String) -> &'a String {
s
}
// или более обобщённо
fn borrow_str_str(s: &'a str) -> &'a str {
s
}
```
Здесь функция не создаёт значение, а лишь возвращает ссылку на переданный объект; borrow checker проверит, что источник живёт достаточно долго.
4) Вернуть умный указатель (разделяемое владение):
```rust
use std::rc::Rc;
fn borrow_str() -> Rc {
Rc::new(String::from("hi"))
}
// или для многопоточности: std::sync::Arc
```
Выдаёт владельца-обёртку, которую можно клонировать для совместного владения.
5) Вернуть Box (передать владение на куче):
```rust
fn borrow_str() -> Box {
Box::new(String::from("hi"))
}
```
Box тоже передаёт владение, но значение хранится на куче.
6) (Хак) Утечка в `'static` — небезопасно в общем случае, но работает:
```rust
fn borrow_str() -> &'static String {
let s = Box::leak(Box::new(String::from("hi")));
s
}
```
Это делает `String` пожизненным (утечёт память) — использовать с осторожностью.
Вывод: нельзя возвращать ссылку на локальную переменную. Выберите подходящий паттерн: возвращать владение (`String`, `Box`, `Rc/Arc`) или ссылку на внешний ресурс (передать его в функцию и использовать lifetime-параметр), либо использовать литерал с `'static`.