#traits #coherence #from #convert #no-alloc #orphan-rules

no-std fromage

A cheesy Rust hack for converting between non-local types

3 unstable releases

Uses new Rust 2024

new 0.1.1 Apr 6, 2025
0.1.0 Apr 6, 2025
0.0.0 Apr 4, 2025

#683 in Rust patterns

Download history 319/week @ 2025-04-01

319 downloads per month

Apache-2.0

10KB
53 lines

ci Documentation Crate

Fromage 🧀

A cheesy Rust hack for converting between non-local types.

TL;DR: Allows implementing From and TryFrom like traits for non-local types without violating the orphan rules.

This crate has no dependencies or macros, is no_std and forbids unsafe code.

Example

Convert between the two non-local types String and usize:

use fromage::{Fromage, TryFromage};

struct X;
impl Fromage<String, X> for usize {
    fn fromage(value: String) -> Self {
        value.len()
    }
}
impl TryFromage<String, X> for usize {
    type Error = ();

    fn try_fromage(value: String) -> Result<Self, Self::Error> {
        Ok(value.len())
    }
}

#[test]
fn test() {
    assert_eq!(5_usize, usize::fromage(String::from("hello")));
    assert_eq!(5_usize, usize::try_fromage(String::from("world")).unwrap());
}

Status

Experimental.

How it works

The orphan rules state that:

> Given impl<P1..=Pn> Trait<T1..=Tn> for T0, an impl is valid only if at least one of the following is true:
>
> - `Trait` is a local trait
> - All of:
>   - At least one of the types T0..=Tn must be a local type. Let Ti be the first such type.
>   - No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti)

Fromage defines the Fromage and TryFromage traits that mirror the traits in the standard library, except that they require an impl defined additional type parameter X and therefore fulfil the "At least one of the types T0..=Tn must be a local type" clause above.

The type parameter X is not used in the trait methods. It may be any type provided it is both local and uncovered, typically a unit struct. It may be reused for all Fromage and TryFromage implementations within a crate.

The Fromage and TryFromage traits define methods named fromage and try_fromage respectively to avoid conflicting with the standard library's From and TryFrom traits.

Limitations

The Fromage and TryFromage traits defined in this crate are distinct from the standard library's From and TryFrom traits and are not interchangeable. Therefore, if a crate uses the standard library's From and TryFrom traits in its public API, you cannot use the Fromage and TryFromage traits to implement conversions for the types required by that crate.

For example, if a crate exposes the following public API then you cannot use an impl Fromage<Foo> for Bar to implement the conversion.

pub fn foo(value: impl Into<Bar>) { ... }

Alternatives

Newtype

Typically, a local newtype is used to implement the standard library's From and TryFrom traits when both types are non-local.

Either:

struct FooWrapper(Foo);

impl From<FooWrapper> for Bar {
    fn from(_value: FooWrapper) -> Self {
        Bar
    }
}

Or:

struct BarWrapper(Bar);

impl From<Foo> for BarWrapper {
    fn from(_value: Foo) -> Self {
        BarWrapper(Bar)
    }
}

Conversion function

Simple conversions functions can be used instead of the From and TryFrom traits.

fn convert(_value: Foo) -> Bar {
    Bar
}
fn try_convert(_value: Foo) -> Result<Bar, ()> {
    Ok(Bar)
}

Local traits

For completeness, the orphan rules allow implementing local MyFrom and MyTryFrom traits which may then be used with non-local types.

This approach is not recommended as it requires a significant amount of boilerplate code whilst sharing the same limitations as the Fromage approach.

License

Fromage is distributed under the terms of the Apache License (Version 2.0).

See LICENSE for details.

Copyright 2025

No runtime deps