Как статически преобразовать `chrono::format::strftime` в`chrono::format::Item`?

У меня есть статический массив chrono::format::strftime форматы, которые поддерживает мое приложение. Я хотел бы избежать их анализа во время выполнения, поэтому я определил lazy_static! блок, который анализирует их в коллекцию chrono::format::Item,

Однако, когда я пытаюсь перебрать разобранную коллекцию, я получаю сообщение об ошибке:

the trait bound `&chrono::format::StrftimeItems<'_>: std::iter::Iterator` is not satisfied

Вот короткая репродукция:

#[macro_use]
extern crate lazy_static;
extern crate chrono;
use chrono::DateTime;
use chrono::offset::FixedOffset;
use chrono::format::{Parsed, parse};
use chrono::format::strftime::StrftimeItems;

static FORMATS : &[&'static str] = &["%Y-%m-%dT%H:%M:%S", "%Y-%m-%dT%H:%M:%S%.f"];

lazy_static! {
    static ref PARSED_FORMATS : Vec<StrftimeItems<'static>> = FORMATS
        .iter()
        .map(|format| StrftimeItems::new(format))
        .collect();
}

fn parse_datetime(s: &str) -> Option<DateTime<FixedOffset>> {
    for format in PARSED_FORMATS.iter() {
        let mut parsed = Parsed::new();
        let dt = parse(&mut parsed, &s, format)
            .and_then(|_| parsed.to_datetime() );
        if dt.is_ok() {
            return dt.ok()
        }
    }
    return None
}

Попытка отмены ссылки format в цикле выдает эту ошибку:

error[E0507]: cannot move out of borrowed content
  --> src\main.rs:21:35
   |
21 |         let dt = parse(&mut parsed, &s, *format)
   |                                         ^^^^^^^ cannot move out of borrowed content

error: aborting due to previous error

Попытка клонировать format кажется, работает, однако клонирование здесь кажется излишним, и я хотел бы избежать этого.

Это правильный подход здесь? или, возможно, использование макроса является лучшим вариантом?

1 ответ

Решение

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

Я хотел бы избежать их анализа во время выполнения

Тем не мение, StrftimeItems не позволяет вам достичь своей цели, потому что StrftimeItems лениво разбирает строку формата по мере продвижения итератора.

Вместо этого я бы предложил вам собрать результаты этого итератора в Vec<Item<'static>>,

#[macro_use]
extern crate lazy_static;
extern crate chrono;
use chrono::DateTime;
use chrono::offset::FixedOffset;
use chrono::format::{Item, Parsed, parse};
use chrono::format::strftime::StrftimeItems;

static FORMATS : &[&'static str] = &["%Y-%m-%dT%H:%M:%S", "%Y-%m-%dT%H:%M:%S%.f"];

lazy_static! {
    static ref PARSED_FORMATS : Vec<Vec<Item<'static>>> = FORMATS
        .iter()
        .map(|format| StrftimeItems::new(format).collect())
        .collect();
}

fn parse_datetime(s: &str) -> Option<DateTime<FixedOffset>> {
    for format in PARSED_FORMATS.iter() {
        let mut parsed = Parsed::new();
        let dt = parse(&mut parsed, &s, format.iter().cloned())
            .and_then(|_| parsed.to_datetime() );
        if dt.is_ok() {
            return dt.ok()
        }
    }
    return None
}

Теперь, когда мы звоним parseмы проходим format.iter().cloned(), format это Vec<Item<'static>>, iter() производит свежий итератор по ссылкам на Itemс и cloned() адаптирует итератор так, чтобы каждый Item возвращается по значению (достигается путем их клонирования), а не по ссылке (потому что parse ожидает итератор Item ценности, а не Item Рекомендации).

Другие вопросы по тегам