Interoperability

Enum Interoperability 

Source
pub enum Interoperability {
    C_COMMON_TRAITS,
    C_CONV_TRAITS,
    C_COLLECT,
    C_SERDE,
    C_SEND_SYNC,
    C_GOOD_ERR,
    C_NUM_FMT,
    C_RW_VALUE,
}

Variants§

§

C_COMMON_TRAITS

Rust’s trait system does not allow orphans: roughly, every impl must live either in the crate that defines the trait or the implementing type. Consequently, crates that define new types should eagerly implement all applicable, common traits.

Types eagerly implement common traits (C-COMMON-TRAITS)

§

C_CONV_TRAITS

The following conversion traits should be implemented where it makes sense: From TryFrom AsRef AsMut

The following conversion traits should never be implemented: Into TryInto.These traits have a blanket impl based on From and TryFrom. Implement those instead.

Examples from the standard library

From<u16> is implemented for u32 because a smaller integer can always be converted to a bigger integer.

From<u32> is not implemented for u16 because the conversion may not be possible if the integer is too big.

TryFrom<u32> is implemented for u16 and returns an error if the integer is too big to fit in u16.

From<Ipv6Addr> is implemented for IpAddr, which is a type that can represent both v4 and v6 IP addresses.

Conversions use the standard traits From, AsRef, AsMut (C-CONV-TRAITS)

§

C_COLLECT

FromIterator and Extend enable collections to be used conveniently with the following iterator methods:

Iterator::collect

Iterator::partition

Iterator::unzip

FromIterator is for creating a new collection containing items from an iterator, and Extend is for adding items from an iterator onto an existing collection.

Examples from the standard library

Vec implements both FromIterator and Extend.

Collections implement FromIterator and Extend (C-COLLECT)

§

C_SERDE

Types that play the role of a data structure should implement Serialize and Deserialize.

There is a continuum of types between things that are clearly a data structure and things that are clearly not, with gray area in between. LinkedHashMap and IpAddr are data structures. It would be completely reasonable for somebody to want to read in a LinkedHashMap or IpAddr from a JSON file, or send one over IPC to another process. LittleEndian is not a data structure. It is a marker used by the byteorder crate to optimize at compile time for bytes in a particular order, and in fact an instance of LittleEndian can never exist at runtime. So these are clear-cut examples; the #rust or #serde IRC channels can help assess more ambiguous cases if necessary.

If a crate does not already depend on Serde for other reasons, it may wish to gate Serde impls behind a Cargo cfg. This way downstream libraries only need to pay the cost of compiling Serde if they need those impls to exist.

Data structures implement Serde’s Serialize, Deserialize (C-SERDE)

§

C_SEND_SYNC

Send and Sync are automatically implemented when the compiler determines it is appropriate.

In types that manipulate raw pointers, be vigilant that the Send and Sync status of your type accurately reflects its thread safety characteristics. Tests like the following can help catch unintentional regressions in whether the type implements Send or Sync.

#[test]
fn test_send() {
    fn assert_send<T: Send>() {}
    assert_send::<MyStrangeType>();
}
#[test]
fn test_sync() {
    fn assert_sync<T: Sync>() {}
    assert_sync::<MyStrangeType>();
}

Types are Send and Sync where possible (C-SEND-SYNC)

§

C_GOOD_ERR

An error type is any type E used in a Result<T, E> returned by any public function of your crate. Error types should always implement the std::error::Error trait which is the mechanism by which error handling libraries like error-chain abstract over different types of errors, and which allows the error to be used as the source() of another error.

Additionally, error types should implement the Send and Sync traits. An error that is not Send cannot be returned by a thread run with thread::spawn. An error that is not Sync cannot be passed across threads using an Arc. These are common requirements for basic error handling in a multithreaded application.

Send and Sync are also important for being able to package a custom error into an IO error using std::io::Error::new, which requires a trait bound of Error + Send + Sync.

One place to be vigilant about this guideline is in functions that return Error trait objects, for example reqwest::Error::get_ref. Typically Error + Send + Sync + ’static will be the most useful for callers. The addition of ’static allows the trait object to be used with Error::downcast_ref.

Never use () as an error type, even where there is no useful additional information for the error to carry.

The error message given by the Display representation of an error type should be lowercase without trailing punctuation, and typically concise.

Error::description() should not be implemented. It has been deprecated and users should always use Display instead of description() to print the error.

Examples of error messages

  1. “unexpected end of file”
  2. “provided string was not true or false
  3. “invalid IP address syntax”
  4. “second time provided was later than self”
  5. “invalid UTF-8 sequence of {} bytes from index {}”
  6. “environment variable was not valid unicode: {:?}”

Error types are meaningful and well-behaved (C-GOOD-ERR)

§

C_NUM_FMT

std::fmt::UpperHex

std::fmt::LowerHex

std::fmt::Octal

std::fmt::Binary

These traits control the representation of a type under the {:X}, {:x}, {:o}, and {:b} format specifiers.

Binary number types provide Hex, Octal, Binary formatting (C-NUM-FMT)

§

C_RW_VALUE

The standard library contains these two impls:

impl<'a, R: Read + ?Sized> Read for &'a mut R { /* ... */ }
impl<'a, W: Write + ?Sized> Write for &'a mut W { /* ... */ }

That means any function that accepts R: Read or W: Write generic parameters by value can be called with a mut reference if necessary.

In the documentation of such functions, briefly remind users that a mut reference can be passed. New Rust users often struggle with this. They may have opened a file and want to read multiple pieces of data out of it, but the function to read one piece consumes the reader by value, so they are stuck. The solution would be to leverage one of the above impls and pass &mut f instead of f as the reader parameter.

Examples

flate2::read::GzDecoder::new

flate2::write::GzEncoder::new

serde_json::from_reader

serde_json::to_writer

Generic reader/writer functions take R: Read and W: Write by value (C-RW-VALUE)

Trait Implementations§

Source§

impl Debug for Interoperability

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.