Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Ловушка: слишком быстрое обращение к dyn Trait

// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0

use std::any::Any;

pub trait AddDyn: Any {
    fn add_dyn(&self, rhs: &dyn AddDyn) -> Box<dyn AddDyn>;
}

impl AddDyn for i32 {
    fn add_dyn(&self, rhs: &dyn AddDyn) -> Box<dyn AddDyn> {
        if let Some(downcast) = (rhs as &dyn Any).downcast_ref::<Self>() {
            Box::new(self + downcast)
        } else {
            Box::new(*self)
        }
    }
}

fn main() {
    let i: &dyn AddDyn = &42;
    let j: &dyn AddDyn = &64;
    let k: Box<dyn AddDyn> = i.add_dyn(j);
    dbg!((k.as_ref() as &dyn Any).is::<i32>());
    dbg!((k.as_ref() as &dyn Any).downcast_ref::<i32>());
}
  • Тем, кто пришёл из ООП, понятно желание как можно раньше взяться за этот инструмент динамической диспетчеризации.

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

  • Пример выше доводит ситуацию до абсурда: если бы сложение чисел было завязано на динамическую диспетчеризацию, сделать что-либо было бы трудно.

    Но во многих языках программирования динамическая диспетчеризация скрыта, а здесь она более явная.

    В реализации AddDyn для i32 сначала нужно попытаться привести аргумент rhs к тому же типу, что и i32; если это не так, операция молча завершается неудачей.

    Затем нужно выделить новое значение в куче, потому что если мы остаёмся в мире динамической диспетчеризации, то это необходимо.

    Сложив два значения, чтобы их вывести, мы должны снова привести их (downcast) к «настоящему» типу, который можно вывести, учитывая ограничения трейтов, связанные с операцией до этого момента.

  • Спросите класс: почему нельзя просто добавить ограничения Display в main, чтобы выводить значения как есть?

    Ответ: потому что add_dyn возвращает только dyn AddDyn, и мы теряем информацию о том, что реализует тип, между типом аргументов и типом результата. Даже если входные значения реализуют Display, тип результата — нет.

  • Это приводит к менее производительному коду, который труднее понять.