about summary refs log tree commit diff
diff options
context:
space:
mode:
authorScott McMurray <scottmcm@users.noreply.github.com>2023-08-06 14:54:55 -0700
committerScott McMurray <scottmcm@users.noreply.github.com>2023-10-05 22:21:45 -0700
commitb80e653ca1278a2c4fa411b938be1ccc7ed204fb (patch)
tree9ceab9e2cf9a547048a33340268e2ae71ea88cdf
parent579be69de9f98f56d92b93820eaf7e6b06b517a5 (diff)
downloadrust-b80e653ca1278a2c4fa411b938be1ccc7ed204fb.tar.gz
rust-b80e653ca1278a2c4fa411b938be1ccc7ed204fb.zip
Attempt to describe the intent behind the `From` trait further
-rw-r--r--library/core/src/convert/mod.rs34
1 files changed, 34 insertions, 0 deletions
diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs
index ff5a4c913b7..db9f7237bd6 100644
--- a/library/core/src/convert/mod.rs
+++ b/library/core/src/convert/mod.rs
@@ -478,6 +478,40 @@ pub trait Into<T>: Sized {
 /// - `From<T> for U` implies [`Into`]`<U> for T`
 /// - `From` is reflexive, which means that `From<T> 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<u16>` exists, where the original
+///   value can be recovered using `u16: TryFrom<i32>`.  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<i16>` *is* available because `1_i16` and `1.0_f32` are conceptually
+///   the same real number (despite having very different bit patterns technically).
+///   `String: From<char>` is available because they're both *text*, but
+///   `String: From<u32>` 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<Ipv6Addr>` exists.
+///
 /// # Examples
 ///
 /// [`String`] implements `From<&str>`: