0

The following compiles and runs fine:

use chrono_tz::Tz;
use chrono::{TimeZone, NaiveDate};
use arrow2::temporal_conversions::parse_offset;

fn my_func(tz: &str) -> (){
    let ndt = NaiveDate::from_ymd_opt(2018, 9, 28).unwrap().and_hms_opt(2, 30, 0).unwrap();
    match parse_offset(&tz) {
        Ok(time_zone) => {
            println!("converted: {:?}", time_zone.from_utc_datetime(&ndt));
        },
        Err(_) => match tz.parse::<Tz>() {
            Ok(time_zone) => {
                println!("converted: {:?}", time_zone.from_utc_datetime(&ndt));
            }
            Err(error) => panic!("Problem opening the file: {:?}", error)
        },
    };
}


fn main() {
    let time_zone = "Asia/Seoul";
    my_func(&time_zone);
}

Noticed though that I'm doing

println!("converted: {:?}", time_zone.from_utc_datetime(&ndt));

twice.

I've tried refactoring to

use chrono_tz::Tz;
use chrono::{TimeZone, NaiveDate};
use arrow2::temporal_conversions::parse_offset;

fn my_func(tz: &str) -> (){
    let ndt = NaiveDate::from_ymd_opt(2018, 9, 28).unwrap().and_hms_opt(2, 30, 0).unwrap();
    let parsed_time_zone: TimeZone = match parse_offset(&tz) {
        Ok(time_zone) => {
            time_zone
        },
        Err(_) => match tz.parse::<Tz>() {
            Ok(time_zone) => {
                time_zone
            }
            Err(error) => panic!("Problem opening the file: {:?}", error)
        },
    };
    println!("converted: {:?}", parsed_time_zone.from_utc_datetime(&ndt));
}


fn main() {
    let time_zone = "Asia/Seoul";
    my_func(&time_zone);
}

and get a long error:

error[E0782]: trait objects must include the `dyn` keyword
 --> src/main.rs:7:27
  |
7 |     let parsed_time_zone: TimeZone = match parse_offset(&tz) {
  |                           ^^^^^^^^
  |
help: add `dyn` keyword before this trait
  |
7 |     let parsed_time_zone: dyn TimeZone = match parse_offset(&tz) {
  |                           +++

error[E0191]: the value of the associated type `Offset` (from trait `TimeZone`) must be specified
 --> src/main.rs:7:27
  |
7 |     let parsed_time_zone: TimeZone = match parse_offset(&tz) {
  |                           ^^^^^^^^ help: specify the associated type: `TimeZone<Offset = Type>`

error[E0038]: the trait `TimeZone` cannot be made into an object
 --> src/main.rs:7:27
  |
7 |     let parsed_time_zone: TimeZone = match parse_offset(&tz) {
  |                           ^^^^^^^^ `TimeZone` cannot be made into an object
  |
  = note: the trait cannot be made into an object because it requires `Self: Sized`
  = note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>

error[E0308]: mismatched types
 --> src/main.rs:9:13
  |
9 |             time_zone
  |             ^^^^^^^^^ expected trait object `dyn TimeZone`, found struct `FixedOffset`
  |
  = note: expected trait object `dyn TimeZone`
                   found struct `FixedOffset`

error[E0308]: mismatched types
  --> src/main.rs:13:17
   |
13 |                 time_zone
   |                 ^^^^^^^^^ expected trait object `dyn TimeZone`, found enum `Tz`
   |
   = note: expected trait object `dyn TimeZone`
                      found enum `Tz`

error: the `from_utc_datetime` method cannot be invoked on a trait object
   --> src/main.rs:18:50
    |
18  |     println!("converted: {:?}", parsed_time_zone.from_utc_datetime(&ndt));
    |                                                  ^^^^^^^^^^^^^^^^^
    |
   ::: /home/marcogorelli/.cargo/registry/src/github.com-1ecc6299db9ec823/chrono-0.4.23/src/offset/mod.rs:205:21
    |
205 | pub trait TimeZone: Sized + Clone {
    |                     ----- this has a `Sized` requirement

Some errors have detailed explanations: E0038, E0191, E0308, E0782.
For more information about an error, try `rustc --explain E0038`.
error: could not compile `tmp` due to 6 previous errors

I've tried the suggestion to do

let parsed_time_zone: dyn TimeZone = match parse_offset(&tz) {

but then it still errors with

error[E0191]: the value of the associated type `Offset` (from trait `TimeZone`) must be specified
 --> src/main.rs:7:31
  |
7 |     let parsed_time_zone: dyn TimeZone = match parse_offset(&tz) {
  |                               ^^^^^^^^ help: specify the associated type: `TimeZone<Offset = Type>`

How can I just parse tz as TimeZone, without specified whether it's Tz or FixedOffset, and then use it in the rest of the function?

ignoring_gravity
  • 6,677
  • 4
  • 32
  • 65

1 Answers1

1

You can't refactor this easily. Rust is strongly typed, and your two time_zone variables aren't the same type. One is chrono::FixedOffset, the other is chrono_tz::Tz.

If you do want to store both of them in the same variable, you have two possibilities:

  • trait object references (&dyn TimeZone)
  • Boxed trait objects (Box<dyn TimeZone).

dyn Timezone itself cannot directly be the type of a variable. It doesn't have a known size, because it could be any actual type of any size. Therefore you need one of the indirections above.

Both are non-ideal for your case though:

  • &dyn TimeZone won't work because someone has to own the actual object. You can't return a reference to a variable of an inner scope, as it gets dropped earlier than the reference does.
  • Box<dyn TimeZone> would work, no problem, but it introduces overhead: It will cause a heap allocation where the actual object will be put. The reason, as described earlier, is that a dyn object doesn't have a known size. Therefore, with Box, the actual object gets moved to the heap, and on the stack is the Box object, which has a known size.

So what I would do is: don't worry. The code duplication you have here is really minimal. I don't think it's worth the trouble.


Further detail I noticed after I wrote this answer:

  • TimeZone is : Sized, which means it isn't object safe and dyn TimeZone is disallowed entirely. So not even Box<dyn TimeZone> would work.
Finomnis
  • 18,094
  • 1
  • 20
  • 27