Есть две проблемы, препятствующие компиляции вашего кода.
Первая ошибка, которую вы увидели: the size for values of type dyn LocationTrait cannot be known at compilation time
, связана с тем, что bson::from_bson
необходимо вернуть результат десериализации по значению. Компилятору нужно знать, сколько места ему нужно выделить в стеке вызовов, чтобы вернуть его.
Однако признаки являются абстракцией для описания поведения, а не данных, поэтому его можно реализовать для u8
(одного байта) или гораздо большей структуры.
Чтобы иметь возможность вернуть такое значение, вам нужно его поставить в рамку (см. Объекты черты ).
Вторая проблема заключается в том, что возвращаемое значение должно реализовывать черту Deserialize
(а не LocationTrait
)
Чтобы решить эту проблему:
Самый простой путь - использовать перечисления вместо черт:
#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Location {
Country(Country),
City(City)
}
Это будет работать с такими документами, как {"type" = "Country", name="usa"}
.
Проверьте Serde doc для дополнительных опций.
Если вы действительно хотите использовать черты (например, чтобы иметь возможность определять типы вне этого модуля), вам понадобится черта в штучной упаковке и пользовательские структуры как таковые:
// The same trait as defined earlier
pub trait LocationTrait {}
impl LocationTrait for Country {}
impl LocationTrait for City {}
// A custom struct on which you can implement the deserialize trait
// Needed as both Deserialize and Box are defined outside this crate.
struct DynLocation(Box<dyn LocationTrait>);
impl<'de> Deserialize<'de> for DynLocation {
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// Tricky part ommited here:
// You will need to partially deserialize you object
// in order to get a first discriminant before instanciating
// and deserializing the proper type.
unimplemented!()
}
}
// The public method to hide the DynLocation wrapper
pub fn deserialize(item: &str) -> Box<dyn LocationTrait> {
let location: DynLocation = serde_json::from_str(item).expect("invalid json");
location.0
}
Некоторое обсуждение по этой же теме можно найти в Как можно добавить десериализацию объектов полиморфных признаков в Rust, если вообще? .