Учитывая следующий код:
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]
"ожидаемая ссылка на фрагмент"?
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
дает вам разыменованный фрагмент, который вы назначаете фрагменту. Это очевидная ошибка типа.
Судя по MIR, созданной первой частью кода, (*f).filter()
эквивалентен f.filter()
; похоже, что компилятор знает, что, поскольку filter
является методом для &self
, разыменование его не служит какой-либо цели и полностью опускается.
Однако второй случай отличается, поскольку разыменование среза вводит код проверки границ. На мой взгляд, компилятор также должен иметь возможность сказать, что эта операция (разыменование) не вносит каких-либо значимых изменений (и / или что не будет ошибки выхода за границы) и рассматривать ее как обычную индексацию среза, но за этим может быть какая-то причина.
Похожие вопросы
Новые вопросы
rust
Rust - это язык системного программирования без сборщика мусора, ориентированный на три цели: безопасность, скорость и параллелизм. Используйте этот тег для вопросов о коде, написанном на Rust. Используйте тег, относящийся к конкретному изданию, для вопросов, относящихся к коду, для которого требуется определенная редакция, например [rust-2018]. Используйте более конкретные теги для таких подтем, как [rust-cargo] и [rust-macros].