Сопоставить зависящее от версии значение перечисления из внешней библиотеки - PullRequest
1 голос
/ 03 августа 2020

TL; DR: в заявлении match возможно ли явно обрабатывать enum варианты, которые могут не существовать? (из-за различных версий внешней библиотеки)

Я пишу код для библиотеки, которая использует оператор match для обработки преобразования объекта из другой библиотеки.

Этот объект приходит из библиотеки geo-types, где geo_types::Geometry может представлять один из нескольких типов объектов:

pub enum Geometry<T> 
where
    T: CoordinateType, 
 {
    Point(Point<T>),
    Line(Line<T>),
    LineString(LineString<T>),
    Polygon(Polygon<T>),
    [...]
}

(каждая из этих структур определена в другом месте в geo-types)

Это другая библиотека (shapefile) реализует черту TryFrom для преобразования определенных geo_types::Geometry вариантов в варианты собственного shapefile::Shape библиотеки, которое является аналогичным перечислением:

pub enum Shape {
    Point(Point),
    Polyline(Polyline),
    Polygon(Polygon),
    [...]
}

Преобразование выполнено в других типажах, поэтому функция try_from() в основном пытается сопоставить совместимые варианты каждого перечисления в блоке сопоставления. Каждый возможный вариант geo_types::Geometry явно сопоставляется с вариантом Shape.

match geometry {
    geo_types::Geometry::Point(point) => Ok(Shape::Point(point.into())),
    geo_types::Geometry::Line(line) => Ok(Shape::Line(line.into())),
    [...]
}

Но в версии 0.6.0 из geo-types добавлено 2 новых варианта перечисления Geometry: Rect и Triangle. Этот оператор match теперь не будет компилироваться:

error[E0004]: non-exhaustive patterns: `Rect(_)` and `Triangle(_)` not covered
   --> src/record/mod.rs:464:15
    |
464 |         match geometry {
    |               ^^^^^^^^ patterns `Rect(_)` and `Triangle(_)` not covered
    | 
   ::: /home/roger/.cargo/registry/src/github.com-1ecc6299db9ec823/geo-types-0.6.0/src/geometry.rs:39:5
    |
39  |     Rect(Rect<T>),
    |     ---- not covered
40  |     Triangle(Triangle<T>),
    |     -------- not covered
    |
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
    = note: the matched value is of type `geo_types::Geometry<f64>`

Я не могу обработать их явно, иначе я нарушу совместимость со старыми версиями geo-types:

error[E0599]: no variant or associated item named `Rect` found for enum `geo_types::Geometry<_>` in the current scope
   --> src/record/mod.rs:479:34
    |
479 |             geo_types::Geometry::Rect(_) => { 
    |                                  ^^^^ variant or associated item not found in `geo_types::Geometry<_>`

I может выполнять подстановочный знак _ => { Err("Unrecognized Geometry") }, который компилируется как для новой, так и для старой версии geo-types, хотя выдает предупреждение, если вы пытаетесь скомпилировать более старую версию geo-types:

warning: unreachable pattern
   --> src/record/mod.rs:480:13
    |
480 |             _ => { // New geometries Rect(_) and Triangle(_) added in 0.6.0
    |             ^
    |
    = note: `#[warn(unreachable_patterns)]` on by default

Но я могу аннотировать #[allow(unreachable_patterns)]. На данный момент это работает, но подавление предупреждения кажется неприятным запахом кода. Кроме того, похоже, что для реализации преобразования Rect или Triangle потребуется нарушение совместимости с некоторыми версиями geo-types (что, в свою очередь, нарушает совместимость с другими библиотеками geographi c, которые полагаются на geo-types).

Есть ли способ дополнительно обработать эти новые варианты перечисления способом, который не зависит от их существования?

1 Ответ

1 голос
/ 03 августа 2020

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

Чтобы решить вашу проблему : Вы будет лучше не беспокоиться о поддержании совместимости со старыми версиями geo-types, по крайней мере, на данный момент.

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

Учитывая, что geo-types API еще не стабилизирован (основная версия по-прежнему 0), обратно несовместимые изменения могут быть внесены в любую версию. Попытки поддержать каждое из этих изменений просто не стоят усилий. Например, весьма вероятно, что существует ряд версий geo-types, которые вводят новый вариант в перечисление Geometry - вы go пытаетесь исправить свой код для поддержки каждого из них этих версий?

Таким образом, лучший вариант для вас - просто поддерживать последнюю версию. Как это обычно бывает с выпусками разработки, другие, использующие библиотеку geo-types, скорее всего, будут использовать последнюю версию geo-types; если нет, они могут просто использовать версию вашей библиотеки, которая поддерживает их версию geo-types. От вас не ожидается, что вы будете много работать с нестабильным API с точки зрения обратной совместимости - только когда API publi c стабилизирован, вам действительно нужно беспокоиться о выпуске и поддержке нескольких основных версий вашей библиотеки для каждой основной версии geo-types.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...