Почему возвращение фрагмента строки вместо размера usize предотвращает изменение строки? - PullRequest
0 голосов
/ 30 мая 2019

Этот код компилируется:

fn main() {
    let mut s = String::from("some_string");
    let n = f1(&s);
    s.clear();
    println!("n = {}", n);
}

fn f1(s: &String) -> usize {
    10
}

fn f2(s: &String) -> &str {
    "def"
}

Однако замена вызова на f1() на f2() приводит к ошибке компиляции. Мне кажется, что и f1(), и f2() делают неизменяемое заимствование, а s.clear() изменяемое заимствование, поэтому я должен получить ошибку компиляции в обоих случаях. Чего мне не хватает?

Ответы [ 3 ]

3 голосов
/ 30 мая 2019

Ссылка Rust говорит :

Если в параметрах используется ровно одно время жизни (исключенное или нет), то это время присваивается всем временам жизни разрешенного выхода.

Это означает, что ваш метод

fn f2(s: &String) -> &str {
    "def"
}

интерпретируется Рустом как:

fn f2<'a>(s: &'a String) -> &'a str {
    "def"
}

Поскольку "def" имеет время жизни 'static, его время жизни может быть сокращено до 'a при возврате из функции (поэтому компилятор здесь не будет жаловаться), но при вызове функции компилятор не может сделать вывод, что истинное время жизни строки было действительно 'static. Для этого вы должны явно пометить его как 'static:

fn f2(s: &String) -> &'static str {
    "def"
}
3 голосов
/ 31 мая 2019

Вот минимальный код, необходимый для воспроизведения проблемы:

fn f1(s: &String) -> usize { unimplemented!() }

fn f2(s: &String) -> &str { unimplemented!() }

fn main() {
    let mut s = String::from("some_string");
    let n = f1(&s);
    s.clear();
    println!("n = {}", n);
}

Анализ времени жизни выполняется на основе сигнатур функций.

Вы заметитев приведенном выше коде, который я использовал unimplemented!() в качестве тела функций, и проблема точно такая же.Это normal .

В подавляющем большинстве случаев 1 сигнатура функции полностью определяет интерфейс функции, и нет необходимости смотреть на ее реализацию.

Как следствие, это также означает, что ли время жизни в возвращаемом типе связано с временем жизни в любом из аргументов, полностью указано в сигнатуре, и в этом случае полная сигнатура f2поэтому:

fn f2<'a>(s: &'a String) -> &'a str;

не имеет значения, является ли реализация f2 "def" (со временем жизни 'static) или &*s (со временем жизни 'a);только подпись имеет значение, и подпись использует тот же срок жизни из-за правил elision.

1 Единственное известное мне исключение касается функции -> impl Trait и того, является лиобъект реализует Send или Sync.


В случае f1 тип возвращаемого значения не связан с аргументом, поэтому заимствование аргумента заканчивается в концевызова f1:

fn main() {
    let mut s = String::from("some_string");
    let n = {
        //  Immutable borrow of s starts here.
        f1(&s)
        //  Immutable borrow of s ends here.
    };
    s.clear();
    println!("n = {}", n);
}

В случае f2 тип возвращаемого значения имеет то же время жизни, что и аргумент, и поэтому считается, что он расширяет заимствование.В Rust 2015 заем будет продлен до тех пор, пока возвращаемая стоимость не выйдет за рамки (лексический заем);с Rust 2018 заем продолжается до последнего использования возвращаемого значения (не лексический заем).

В вашем случае оба в основном идентичны:

fn main() {
    let mut s = String::from("some_string");
    let n = {
        //  Immutable borrow of s starts here.
        f2(&s)
    };
    s.clear();  //  Conflicting attempt to mutably borrow s.
    println!("n = {}", n);
    //  Immutable borrow of s ends here.
}

Вы могли наблюдатьРазница путем переключения порядка s.clear() и println!.

1 голос
/ 30 мая 2019

f1 и f2 оба берут неизменный заем. Однако срок действия заимствования из f1 заканчивается в конце f1, потому что вы просто возвращаете usize, а не что-либо из фактической строки.

Однако f2 возвращает &str, который заимствует ваш базовый String, s. Поскольку n остается в живых, неизменный заем s продолжается до тех пор, пока n больше не используется. По сути, это препятствует тому, чтобы ваш звонок на s.clear() «вытащил коврик из-под» вашего указателя s.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...