betrunken
betrunken

Reputation: 114

Calling a method on a generic type in Rust

I'm new to Rust, and a bit confused about the usage of generic types.

Consider the below scenario:

I can create a Date type like so:

use chrono::prelude::*;

fn main() {
  let date = Utc.ymd(2021, 1, 1);
}

Now if I create a function foo to do something similar:

use chrono::prelude::*;

fn foo<Tz: TimeZone>(date: Date<Tz>) {
  let other_date = Tz.ymd(2020, 1, 1);
}


fn main() {
  let date = Utc.ymd(2021, 1, 1);

  foo(date);
}

I expected Tz to be substituted by Utc, and Tz.ymd to be equivalent to Utc.ymd, but it's not so:

error[E0423]: expected value, found type parameter `Tz`
 --> src/main.rs:4:20
  |
4 |   let other_date = Tz.ymd(2020, 1, 1);
  |                    ^^ not a value

Which makes me think it needs to be instantiated first? But how come that wasn't needed in the first place? What am I misunderstanding here?

Upvotes: 2

Views: 290

Answers (1)

trent
trent

Reputation: 27895

chrono::offset::Utc is a unit-like struct. This means that it is both the name of a type with a single value and the name of the value itself, like () is both a type and a value. This is a convenience feature but it can sometimes be confusing.

In Utc.ymd(2021, 1, 1) you are using Utc as a value. Only values can use . syntax.¹ However, in foo, Tz is a type parameter. It could be any type that implements TimeZone, so you can't create a value of type Tz out of thin air. However, what you can do is take the timezone of the Date<Tz> which was passed in:

fn foo<Tz: TimeZone>(date: Date<Tz>) {
    let other_date = date.timezone().ymd(2020, 1, 1);
}

This clones the Tz that is part of date and uses the clone to create other_date.


¹ The fully-qualified version of Utc.ymd(2021, 1, 1) is <Utc as TimeZone>::ymd(&Utc, 2021, 1, 1) where the first Utc is the type Utc and the second one is the value Utc.

Upvotes: 5

Related Questions