Repository: time-rs/time (language: rust)

Issue:
`OffsetDateTime::format` (and `::format_into` and `::parse`) should allow `?Sized` for the `Formattable` (and `Parsable`) bound
https://github.com/time-rs/time/blob/d700977ce9bc34013ee9841e122edbc807f410e4/src/offset_date_time.rs#L808

This **can't** be used like this:

```rust
const FORMAT: &[FormatItem<'_>] = time::macros::format_description!("...");

time.format(FORMAT)
```

... even though `[FormatItem<'_>]: Formattable` and thus `&[FormatItem<'_>]` is `&impl Formattable`. This is because the `impl Formattable` bound disallows `!Sized` types like `[FormatItem<'_>]`

It should be `pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {`, and the same for `format_into` and `parse`

Compare `foo` and `foo2` in   https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=72a25a6049efc8d16f374a460cfa0dce

---

Workaround: write `time.format(&FORMAT)` instead, which works through the blanket impl for `Deref`.


Discussion / hints:

Fix patch:
diff --git a/benchmarks/formatting.rs b/benchmarks/formatting.rs
index 5ea4d1d44..c2688c84b 100644
--- a/benchmarks/formatting.rs
+++ b/benchmarks/formatting.rs
@@ -151,7 +151,7 @@ setup_benchmark! {
         ben.iter(|| {
             datetime!(1970-01-01 0:00).format_into(
                 &mut io::sink(),
-                &fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"),
+                fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"),
             )
         });
     }
diff --git a/src/date.rs b/src/date.rs
index f3885d966..a730f2760 100644
--- a/src/date.rs
+++ b/src/date.rs
@@ -657,7 +657,7 @@ impl Date {
     pub fn format_into(
         self,
         output: &mut impl io::Write,
-        format: &impl Formattable,
+        format: &(impl Formattable + ?Sized),
     ) -> Result<usize, error::Format> {
         format.format_into(output, Some(self), None, None)
     }
@@ -670,7 +670,7 @@ impl Date {
     /// assert_eq!(date!(2020 - 01 - 02).format(&format)?, "2020-01-02");
     /// # Ok::<_, time::Error>(())
     /// ```
-    pub fn format(self, format: &impl Formattable) -> Result<String, error::Format> {
+    pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
         format.format(Some(self), None, None)
     }
 }
@@ -687,7 +687,10 @@ impl Date {
     /// assert_eq!(Date::parse("2020-01-02", &format)?, date!(2020 - 01 - 02));
     /// # Ok::<_, time::Error>(())
     /// ```
-    pub fn parse(input: &str, description: &impl Parsable) -> Result<Self, error::Parse> {
+    pub fn parse(
+        input: &str,
+        description: &(impl Parsable + ?Sized),
+    ) -> Result<Self, error::Parse> {
         description.parse_date(input.as_bytes())
     }
 }
diff --git a/src/offset_date_time.rs b/src/offset_date_time.rs
index 4979371aa..c248d9286 100644
--- a/src/offset_date_time.rs
+++ b/src/offset_date_time.rs
@@ -779,7 +779,7 @@ impl OffsetDateTime {
     pub fn format_into(
         self,
         output: &mut impl io::Write,
-        format: &impl Formattable,
+        format: &(impl Formattable + ?Sized),
     ) -> Result<usize, error::Format> {
         let local = self.utc_datetime.utc_to_offset(self.offset);
         format.format_into(
@@ -805,7 +805,7 @@ impl OffsetDateTime {
     /// );
     /// # Ok::<_, time::Error>(())
     /// ```
-    pub fn format(self, format: &impl Formattable) -> Result<String, error::Format> {
+    pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
         let local = self.utc_datetime.utc_to_offset(self.offset);
         format.format(Some(local.date), Some(local.time), Some(self.offset))
     }
@@ -829,7 +829,10 @@ impl OffsetDateTime {
     /// );
     /// # Ok::<_, time::Error>(())
     /// ```
-    pub fn parse(input: &str, description: &impl Parsable) -> Result<Self, error::Parse> {
+    pub fn parse(
+        input: &str,
+        description: &(impl Parsable + ?Sized),
+    ) -> Result<Self, error::Parse> {
         description.parse_offset_date_time(input.as_bytes())
     }
 }
diff --git a/src/primitive_date_time.rs b/src/primitive_date_time.rs
index 89456c7de..538a0388b 100644
--- a/src/primitive_date_time.rs
+++ b/src/primitive_date_time.rs
@@ -507,7 +507,7 @@ impl PrimitiveDateTime {
     pub fn format_into(
         self,
         output: &mut impl io::Write,
-        format: &impl Formattable,
+        format: &(impl Formattable + ?Sized),
     ) -> Result<usize, error::Format> {
         format.format_into(output, Some(self.date), Some(self.time), None)
     }
@@ -524,7 +524,7 @@ impl PrimitiveDateTime {
     /// );
     /// # Ok::<_, time::Error>(())
     /// ```
-    pub fn format(self, format: &impl Formattable) -> Result<String, error::Format> {
+    pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
         format.format(Some(self.date), Some(self.time), None)
     }
 }
@@ -544,7 +544,10 @@ impl PrimitiveDateTime {
     /// );
     /// # Ok::<_, time::Error>(())
     /// ```
-    pub fn parse(input: &str, description: &impl Parsable) -> Result<Self, error::Parse> {
+    pub fn parse(
+        input: &str,
+        description: &(impl Parsable + ?Sized),
+    ) -> Result<Self, error::Parse> {
         description.parse_date_time(input.as_bytes())
     }
 }
diff --git a/src/time.rs b/src/time.rs
index 6912f3da8..e640c0520 100644
--- a/src/time.rs
+++ b/src/time.rs
@@ -415,7 +415,7 @@ impl Time {
     pub fn format_into(
         self,
         output: &mut impl io::Write,
-        format: &impl Formattable,
+        format: &(impl Formattable + ?Sized),
     ) -> Result<usize, crate::error::Format> {
         format.format_into(output, None, Some(self), None)
     }
@@ -428,7 +428,10 @@ impl Time {
     /// assert_eq!(time!(12:00).format(&format)?, "12:00:00");
     /// # Ok::<_, time::Error>(())
     /// ```
-    pub fn format(self, format: &impl Formattable) -> Result<String, crate::error::Format> {
+    pub fn format(
+        self,
+        format: &(impl Formattable + ?Sized),
+    ) -> Result<String, crate::error::Format> {
         format.format(None, Some(self), None)
     }
 }
@@ -445,7 +448,10 @@ impl Time {
     /// assert_eq!(Time::parse("12:00:00", &format)?, time!(12:00));
     /// # Ok::<_, time::Error>(())
     /// ```
-    pub fn parse(input: &str, description: &impl Parsable) -> Result<Self, error::Parse> {
+    pub fn parse(
+        input: &str,
+        description: &(impl Parsable + ?Sized),
+    ) -> Result<Self, error::Parse> {
         description.parse_time(input.as_bytes())
     }
 }
diff --git a/src/utc_offset.rs b/src/utc_offset.rs
index 0dfc00c29..4f9704fb6 100644
--- a/src/utc_offset.rs
+++ b/src/utc_offset.rs
@@ -260,7 +260,7 @@ impl UtcOffset {
     pub fn format_into(
         self,
         output: &mut impl io::Write,
-        format: &impl Formattable,
+        format: &(impl Formattable + ?Sized),
     ) -> Result<usize, error::Format> {
         format.format_into(output, None, None, Some(self))
     }
@@ -273,7 +273,7 @@ impl UtcOffset {
     /// assert_eq!(offset!(+1).format(&format)?, "+01:00");
     /// # Ok::<_, time::Error>(())
     /// ```
-    pub fn format(self, format: &impl Formattable) -> Result<String, error::Format> {
+    pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
         format.format(None, None, Some(self))
     }
 }
@@ -290,7 +290,10 @@ impl UtcOffset {
     /// assert_eq!(UtcOffset::parse("-03:42", &format)?, offset!(-3:42));
     /// # Ok::<_, time::Error>(())
     /// ```
-    pub fn parse(input: &str, description: &impl Parsable) -> Result<Self, error::Parse> {
+    pub fn parse(
+        input: &str,
+        description: &(impl Parsable + ?Sized),
+    ) -> Result<Self, error::Parse> {
         description.parse_offset(input.as_bytes())
     }
 }


Test patch:
diff --git a/tests/integration/error.rs b/tests/integration/error.rs
index 2132c956f..064dedc7e 100644
--- a/tests/integration/error.rs
+++ b/tests/integration/error.rs
@@ -42,7 +42,7 @@ fn insufficient_type_information() -> Format {
 }
 
 fn unexpected_trailing_characters() -> Parse {
-    Time::parse("a", &format_description!("")).unwrap_err()
+    Time::parse("a", format_description!("")).unwrap_err()
 }
 
 fn invalid_format_description() -> InvalidFormatDescription {
diff --git a/tests/integration/formatting.rs b/tests/integration/formatting.rs
index 9b047fb81..68821dde7 100644
--- a/tests/integration/formatting.rs
+++ b/tests/integration/formatting.rs
@@ -105,55 +105,55 @@ fn format_time() -> time::Result<()> {
 
     for &(format_description, output) in &format_output {
         assert_eq!(
-            time!(13:02:03.456_789_012).format(&format_description)?,
+            time!(13:02:03.456_789_012).format(format_description)?,
             output
         );
         assert!(
             time!(13:02:03.456_789_012)
-                .format_into(&mut io::sink(), &format_description)
+                .format_into(&mut io::sink(), format_description)
                 .is_ok()
         );
     }
 
     assert_eq!(
-        time!(1:02:03).format(&fd!("[hour repr:12][period]"))?,
+        time!(1:02:03).format(fd!("[hour repr:12][period]"))?,
         "01AM"
     );
     assert_eq!(
-        Time::MIDNIGHT.format(&fd!("[hour repr:12][period case:lower]"))?,
+        Time::MIDNIGHT.format(fd!("[hour repr:12][period case:lower]"))?,
         "12am"
     );
-    assert_eq!(Time::MIDNIGHT.format(&fd!("[subsecond digits:1+]"))?, "0");
+    assert_eq!(Time::MIDNIGHT.format(fd!("[subsecond digits:1+]"))?, "0");
     assert_eq!(
-        time!(0:00:00.01).format(&fd!("[subsecond digits:1+]"))?,
+        time!(0:00:00.01).format(fd!("[subsecond digits:1+]"))?,
         "01"
     );
     assert_eq!(
-        time!(0:00:00.001).format(&fd!("[subsecond digits:1+]"))?,
+        time!(0:00:00.001).format(fd!("[subsecond digits:1+]"))?,
         "001"
     );
     assert_eq!(
-        time!(0:00:00.0001).format(&fd!("[subsecond digits:1+]"))?,
+        time!(0:00:00.0001).format(fd!("[subsecond digits:1+]"))?,
         "0001"
     );
     assert_eq!(
-        time!(0:00:00.00001).format(&fd!("[subsecond digits:1+]"))?,
+        time!(0:00:00.00001).format(fd!("[subsecond digits:1+]"))?,
         "00001"
     );
     assert_eq!(
-        time!(0:00:00.000001).format(&fd!("[subsecond digits:1+]"))?,
+        time!(0:00:00.000001).format(fd!("[subsecond digits:1+]"))?,
         "000001"
     );
     assert_eq!(
-        time!(0:00:00.0000001).format(&fd!("[subsecond digits:1+]"))?,
+        time!(0:00:00.0000001).format(fd!("[subsecond digits:1+]"))?,
         "0000001"
     );
     assert_eq!(
-        time!(0:00:00.00000001).format(&fd!("[subsecond digits:1+]"))?,
+        time!(0:00:00.00000001).format(fd!("[subsecond digits:1+]"))?,
         "00000001"
     );
     assert_eq!(
-        time!(0:00:00.000000001).format(&fd!("[subsecond digits:1+]"))?,
+        time!(0:00:00.000000001).format(fd!("[subsecond digits:1+]"))?,
         "000000001"
     );
 
@@ -205,10 +205,10 @@ fn format_date() -> time::Result<()> {
     ];
 
     for &(format_description, output) in &format_output {
-        assert_eq!(date!(2019 - 12 - 31).format(&format_description)?, output);
+        assert_eq!(date!(2019 - 12 - 31).format(format_description)?, output);
         assert!(
             date!(2019 - 12 - 31)
-                .format_into(&mut io::sink(), &format_description)
+                .format_into(&mut io::sink(), format_description)
                 .is_ok()
         );
     }
@@ -257,10 +257,10 @@ fn format_offset() -> time::Result<()> {
     ];
 
     for &(value, format_description, output) in &value_format_output {
-        assert_eq!(value.format(&format_description)?, output);
+        assert_eq!(value.format(format_description)?, output);
         assert!(
             value
-                .format_into(&mut io::sink(), &format_description)
+                .format_into(&mut io::sink(), format_description)
                 .is_ok()
         );
     }
@@ -286,12 +286,12 @@ fn format_pdt() -> time::Result<()> {
     let format_description = fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]");
 
     assert_eq!(
-        datetime!(1970-01-01 0:00).format(&format_description)?,
+        datetime!(1970-01-01 0:00).format(format_description)?,
         "1970-01-01 00:00:00.0"
     );
     assert!(
         datetime!(1970-01-01 0:00)
-            .format_into(&mut io::sink(), &format_description)
+            .format_into(&mut io::sink(), format_description)
             .is_ok()
     );
 
@@ -346,7 +346,7 @@ fn insufficient_type_information() {
             Err(time::error::Format::InsufficientTypeInformation { .. })
         ));
     };
-    assert_insufficient_type_information(Time::MIDNIGHT.format(&fd!("[year]")));
+    assert_insufficient_type_information(Time::MIDNIGHT.format(fd!("[year]")));
     assert_insufficient_type_information(Time::MIDNIGHT.format(&Rfc3339));
     assert_insufficient_type_information(date!(2021 - 001).format(&Rfc3339));
     assert_insufficient_type_information(datetime!(2021 - 001 0:00).format(&Rfc3339));
@@ -366,7 +366,7 @@ fn failed_write() -> time::Result<()> {
             matches!(res, Err(time::error::Format::StdIo(e)) if e.kind() == ErrorKind::WriteZero)
         );
     };
-    assert_err(Time::MIDNIGHT.format_into(bytes!(0), &fd!("foo")));
+    assert_err(Time::MIDNIGHT.format_into(bytes!(0), fd!("foo")));
     assert_err(Time::MIDNIGHT.format_into(bytes!(0), &FormatItem::Compound(fd!("foo"))));
     assert_err(OffsetDateTime::UNIX_EPOCH.format_into(bytes!(0), &Rfc3339));
     assert_err(OffsetDateTime::UNIX_EPOCH.format_into(bytes!(4), &Rfc3339));
@@ -386,15 +386,15 @@ fn failed_write() -> time::Result<()> {
     assert_err(datetime!(2021-001 0:00 +0:01).format_into(bytes!(20), &Rfc3339));
     assert_err(datetime!(2021-001 0:00 +0:01).format_into(bytes!(22), &Rfc3339));
     assert_err(datetime!(2021-001 0:00 +0:01).format_into(bytes!(23), &Rfc3339));
-    assert_err(Time::MIDNIGHT.format_into(bytes!(0), &fd!("[hour padding:space]")));
-    assert_err(Time::MIDNIGHT.format_into(bytes!(1), &fd!("[hour padding:space]")));
-    assert_err(offset!(+1).format_into(bytes!(0), &fd!("[offset_hour sign:mandatory]")));
-    assert_err(offset!(-1).format_into(bytes!(0), &fd!("[offset_hour]")));
-    assert_err(offset!(-1).format_into(bytes!(1), &fd!("[offset_hour]")));
-    assert_err(date!(-1 - 001).format_into(bytes!(0), &fd!("[year]")));
-    assert_err(date!(2021 - 001).format_into(bytes!(0), &fd!("[year sign:mandatory]")));
-    assert_err(date!(+999_999 - 001).format_into(bytes!(4), &fd!("[year]")));
-    assert_err(date!(+99_999 - 001).format_into(bytes!(4), &fd!("[year]")));
+    assert_err(Time::MIDNIGHT.format_into(bytes!(0), fd!("[hour padding:space]")));
+    assert_err(Time::MIDNIGHT.format_into(bytes!(1), fd!("[hour padding:space]")));
+    assert_err(offset!(+1).format_into(bytes!(0), fd!("[offset_hour sign:mandatory]")));
+    assert_err(offset!(-1).format_into(bytes!(0), fd!("[offset_hour]")));
+    assert_err(offset!(-1).format_into(bytes!(1), fd!("[offset_hour]")));
+    assert_err(date!(-1 - 001).format_into(bytes!(0), fd!("[year]")));
+    assert_err(date!(2021 - 001).format_into(bytes!(0), fd!("[year sign:mandatory]")));
+    assert_err(date!(+999_999 - 001).format_into(bytes!(4), fd!("[year]")));
+    assert_err(date!(+99_999 - 001).format_into(bytes!(4), fd!("[year]")));
 
     let component_names = [
         "day",