From b80e653ca1278a2c4fa411b938be1ccc7ed204fb Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 6 Aug 2023 14:54:55 -0700 Subject: [PATCH 1/3] Attempt to describe the intent behind the `From` trait further --- library/core/src/convert/mod.rs | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index ff5a4c913b7ed..db9f7237bd6c3 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -478,6 +478,40 @@ pub trait Into: Sized { /// - `From for U` implies [`Into`]` for T` /// - `From` is reflexive, which means that `From for T` is implemented /// +/// # When to implement `From` +/// +/// While there's no technical restrictions on which conversions can be done using +/// a `From` implementation, the general expectation is that the conversions +/// should typically be restricted as follows: +/// +/// * The conversion is *lossless*: it cannot fail and it's possible to recover +/// the original value. For example, `i32: From` exists, where the original +/// value can be recovered using `u16: TryFrom`. And `String: From<&str>` +/// exists, where you can get something equivalent to the original value via +/// `Deref`. But `From` cannot be used to convert from `u32` to `u16`, since +/// that cannot succeed in a lossless way. +/// +/// * The conversion is *value-preserving*: the conceptual kind and meaning of +/// the resulting value is the same, even though the Rust type and technical +/// representation might be different. For example `-1_i8 as u8` is *lossless*, +/// since `as` casting back can recover the original value, but that conversion +/// is *not* available via `From` because `-1` and `255` are different conceptual +/// values (despite being identical bit patterns technically). But +/// `f32: From` *is* available because `1_i16` and `1.0_f32` are conceptually +/// the same real number (despite having very different bit patterns technically). +/// `String: From` is available because they're both *text*, but +/// `String: From` is *not* available, since `1` (a number) and `"1"` +/// (text) are too different. (Converting values to text is instead covered +/// by the [`Display`](crate::fmt::Display) trait.) +/// +/// * The conversion is *obvious*: it's the only reasonable conversion between +/// the two types. Otherwise it's better to have it be a named method or +/// constructor, like how [`str::as_bytes`] is a method and how integers have +/// methods like [`u32::from_ne_bytes`], [`u32::from_le_bytes`], and +/// [`u32::from_be_bytes`], none of which are `From` implementations. Whereas +/// there's only one reasonable way to wrap an [`Ipv6Addr`](crate::net::Ipv6Addr) +/// into an [`IpAddr`](crate::net::IpAddr), thus `IpAddr: From` exists. +/// /// # Examples /// /// [`String`] implements `From<&str>`: From 44f92c1f805434866b9744a6c8953ecdd8cc36f9 Mon Sep 17 00:00:00 2001 From: scottmcm Date: Fri, 6 Oct 2023 05:31:54 +0000 Subject: [PATCH 2/3] Don't mention "recover the original" in `From` docs Co-authored-by: Josh Triplett --- library/core/src/convert/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index db9f7237bd6c3..aed5552ef91f0 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -484,8 +484,11 @@ pub trait Into: Sized { /// a `From` implementation, the general expectation is that the conversions /// should typically be restricted as follows: /// -/// * The conversion is *lossless*: it cannot fail and it's possible to recover -/// the original value. For example, `i32: From` exists, where the original +/// * The conversion is *infallible*: if the conversion can fail, use `TryFrom` +/// instead; don't provide a `From` impl that panics. +/// +/// * The conversion is *lossless*: semantically, it should not lose or discard +/// information. For example, `i32: From` exists, where the original /// value can be recovered using `u16: TryFrom`. And `String: From<&str>` /// exists, where you can get something equivalent to the original value via /// `Deref`. But `From` cannot be used to convert from `u32` to `u16`, since From 1651f1f4b8f97a51f1699101cfe03ea129ec7a07 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 5 Oct 2023 23:03:02 -0700 Subject: [PATCH 3/3] Elaborate some caveats to lossless --- library/core/src/convert/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index aed5552ef91f0..c4b867a480914 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -484,7 +484,7 @@ pub trait Into: Sized { /// a `From` implementation, the general expectation is that the conversions /// should typically be restricted as follows: /// -/// * The conversion is *infallible*: if the conversion can fail, use `TryFrom` +/// * The conversion is *infallible*: if the conversion can fail, use [`TryFrom`] /// instead; don't provide a `From` impl that panics. /// /// * The conversion is *lossless*: semantically, it should not lose or discard @@ -492,7 +492,10 @@ pub trait Into: Sized { /// value can be recovered using `u16: TryFrom`. And `String: From<&str>` /// exists, where you can get something equivalent to the original value via /// `Deref`. But `From` cannot be used to convert from `u32` to `u16`, since -/// that cannot succeed in a lossless way. +/// that cannot succeed in a lossless way. (There's some wiggle room here for +/// information not considered semantically relevant. For example, +/// `Box<[T]>: From>` exists even though it might not preserve capacity, +/// like how two vectors can be equal despite differing capacities.) /// /// * The conversion is *value-preserving*: the conceptual kind and meaning of /// the resulting value is the same, even though the Rust type and technical