В чем разница между разыменованием необработанного указателя на String и необработанного указателя на i32?

fn func(s: *mut String, a: *mut i32) -> usize {
    println!("{}", unsafe { *s });
    println!("{}", unsafe { *a });

    unsafe { (*s).len() }
}

fn main() {
    let mut s = String::from("hello");
    let mut a = 10;

    func(&mut s, &mut a);
}

Приведенный выше код не работает с ошибкой:

error[E0507]: cannot move out of dereference of raw pointer
 --> src/main.rs:2:29
  |
2 |     println!("{}", unsafe { *s });
  |                             ^^ cannot move out of dereference of raw pointer

Почему это происходит для String, а не для i32? Почему он жалуется на «переезд»?


person soupybionics    schedule 19.12.2017    source источник
comment
Почему он жалуется на перемещение - если вы не знаете, что означает перемещение в Rust, вам , вероятно, пока не следует использовать необработанные указатели. Вернитесь и перечитайте всю вводную книгу Язык программирования Rust   -  person Shepmaster    schedule 19.12.2017
comment
Вы можете использовать std::ptr::read для копирования необработанных строковых данных, но это может легко привести к неопределенному поведению.   -  person CodesInChaos    schedule 22.12.2017


Ответы (1)


Почему это происходит для String, а не для i32?

Базовые интегральные типы (и многие другие типы) в Rust реализуют Copy черта. У них есть «семантика копирования», а не «семантика перемещения». Здесь нет смены владельца ... вы копируете значение. String не реализует признак Copy, поэтому эта привязка имеет "семантику перемещения".

Это не уникально для необработанных указателей и не имеет ничего общего с их изменчивостью. Этот пример показывает, что это может происходить с неизменяемыми ссылками:

fn func(s: &String, a: &i32) {
    let _x = *s;
    let _x = *a;
}

Почему он жалуется на «переезд»?

Это происходит потому, что вы пытаетесь переместить владение из блока unsafe. Пока вы не заботитесь об этом, вам нужно содержать «ход» в блоке unsafe, чтобы компилятор просто позволял вам выстрелить себе в ногу. Таким образом, если вы реструктурируете свой код, чтобы не выходить за пределы блока unsafe, код будет компилироваться:

unsafe {
    println!("{}", *s);
}

Вот он работает на игровой площадке.

Однако, чтобы повторить точку зрения Шепмастера в комментарии к вашему вопросу ... если термин "перемещение" звучит для вас чуждо, тогда вам не следует использовать необработанные указатели / блоки unsafe в первую очередь, а вместо этого следует вернуться к доступной документации чтобы Rust понял эту концепцию ... поскольку она является основной.

person Simon Whitehead    schedule 19.12.2017
comment
Значит, при вызове println! () Происходит передача права собственности? Я имею в виду, почему это работает для `let x = String :: from (hello); println! ({}, х); println! ({}, х); `. Я немного сбит с толку, почему тогда удается выполнить второй println! ()? Также не могли бы вы подробнее рассказать о том, что это происходит потому, что вы пытаетесь переместить владение из небезопасного блока. Пожалуйста, терпи мои глупые вопросы - person soupybionics; 19.12.2017
comment
@soupybionics println! не получает права собственности (фактически, он автоматически ссылается на свои аргументы ), но значение нужно переместить изнутри блока наружу. Вот пример той же проблемы без каких-либо unsafe. {} блокировка сил ход, превратив значение *s во временное, как это сделал блок unsafe. println!("{}", *s); будет работать. (Но также println!("{}", s).) - person trentcl; 19.12.2017
comment
@trentcl, спасибо! Я до сих пор не слежу за ним. В чем разница между unsafe {* s; } и небезопасно {println! ({}, * s); } - person soupybionics; 19.12.2017
comment
@soupybionics Разница в том, что println! автоматически ссылается на свои аргументы, поэтому перед *s есть неявное &. Первые вопросы и ответы, которые я связал, объясняют это. - person trentcl; 20.12.2017
comment
Большое спасибо trentcl! Думаю, теперь я понял суть. По сути, код let a = String::from("abc"); a; println("{}", a); запускает движение (передачу права собственности), что приводит к ошибке в println!, как и ожидалось. Таким образом, в случае unsafe { *s; } происходит переход к временному режиму (снова передача права собственности), как в приведенном выше примере abc. Больше ничего. Надеюсь, я правильно понял :) - person soupybionics; 21.12.2017
comment
Небольшая поправка. Вместо того, чтобы сказать, что есть движение, я считаю правильным сказать, что он скорее не разрешает движение, учитывая ошибки cannot move out of borrowed context или cannot move out of dereference of raw pointer - person soupybionics; 21.12.2017