Учитывая следующий код:

trait Function {
    fn filter (&self);
}

#[derive(Debug, Copy, Clone)]
struct Kidney {}

impl Function for Kidney {
    fn filter (&self)  {
        println!("filtered");
    }  
}

fn main() {
    let k = Kidney {};
    let f: &Function = &k;
    //let k1 = (*f);   //--> This gives a "size not satisfied" error
    (*f).filter();     //--> Works; what exactly happens here?
}

Я не уверен, почему он компилируется. Я ожидал, что последнее утверждение не удастся. Полагаю, я упустил из виду некоторые основы при изучении Rust, так как не понимаю, почему разыменование трейта (живущего за указателем) должно компилироваться.

Эта проблема похожа на следующий случай?

let v = vec![1, 2, 3, 4];
//let s: &[i32] = *v;
println!("{}", (*v)[0]);

*v дает срез, но срез не имеет размера, поэтому мне снова не ясно, как это компилируется. Если я раскомментирую второе утверждение, я получу

   |     let s:&[i32]= *v;
   |                   ^^
   |                   |
   |                   expected &[i32], found slice
   |                   help: consider borrowing here: `&*v`
   |
   = note: expected type `&[i32]`
              found type `[{integer}]`

Означает ли expected type &[i32] "ожидаемая ссылка на фрагмент"?

1
soupybionics 14 Мар 2018 в 10:07

2 ответа

Лучший ответ

Разыменовывание объекта-признака не проблема. Фактически, в какой-то момент его необходимо разыменовать, иначе это было бы совершенно бесполезно.

let k1 = (*f); терпит неудачу не из-за разыменования, а из-за того, что вы пытаетесь поместить необработанный объект признака в стек (здесь находятся локальные переменные). Значения в стеке должны иметь размер, известный во время компиляции, что не относится к объектам признаков, потому что любой тип может реализовать признак.

Вот пример, в котором это свойство реализуют структуры разных размеров:

trait Function {
    fn filter (&self);
}

#[derive(Debug, Copy, Clone)]
struct Kidney {}

impl Function for Kidney {
    fn filter (&self)  {
        println!("filtered");
    }  
}

#[derive(Debug, Copy, Clone)]
struct Liver {
    size: f32
}

impl Function for Liver {
    fn filter (&self)  {
        println!("filtered too!");
    }  
}

fn main() {
    let k = Kidney {};
    let l = Liver {size: 1.0};

    let f: &Function;
    if true {
        f = &k;
    } else {
        f = &l;
    }

    // Now what is the size of *f - Kidney (0 bytes) or Liver (4 bytes)?
}

(*f).filter(); работает, потому что временно разыменованный объект не помещается в стек. Фактически, это то же самое, что и f.filter(). Rust автоматически применяет столько разыменований, сколько требуется, чтобы добраться до реального объекта. Это задокументировано в книге.

Во втором случае происходит следующее: {{X0} } реализует Deref в срезы, поэтому все методы реализованы для срезов бесплатно. *v дает вам разыменованный фрагмент, который вы назначаете фрагменту. Это очевидная ошибка типа.

3
kazemakase 15 Мар 2018 в 07:57

Судя по MIR, созданной первой частью кода, (*f).filter() эквивалентен f.filter(); похоже, что компилятор знает, что, поскольку filter является методом для &self, разыменование его не служит какой-либо цели и полностью опускается.

Однако второй случай отличается, поскольку разыменование среза вводит код проверки границ. На мой взгляд, компилятор также должен иметь возможность сказать, что эта операция (разыменование) не вносит каких-либо значимых изменений (и / или что не будет ошибки выхода за границы) и рассматривать ее как обычную индексацию среза, но за этим может быть какая-то причина.

1
Shepmaster 14 Мар 2018 в 13:07