An open API service indexing awesome lists of open source software.

https://github.com/fujiapple852/fromage

A cheesy Rust hack for converting between non-local types
https://github.com/fujiapple852/fromage

cheese coherence conversion hack orphan rust rust-lang trait

Last synced: about 2 months ago
JSON representation

A cheesy Rust hack for converting between non-local types

Awesome Lists containing this project

README

          

![ci](https://github.com/fujiapple852/fromage/actions/workflows/ci.yml/badge.svg)
[![Documentation](https://docs.rs/fromage/badge.svg)](https://docs.rs/fromage/0.1.1)
[![Crate](https://img.shields.io/crates/v/fromage.svg)](https://crates.io/crates/fromage/0.1.1)

# 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](https://doc.rust-lang.org/reference/items/implementations.html#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`:

```rust
use fromage::{Fromage, TryFromage};

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

fn try_fromage(value: String) -> Result {
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](https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules) state that:

```
> Given impl Trait 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](https://doc.rust-lang.org/reference/glossary.html#local-type)
and [uncovered](https://doc.rust-lang.org/reference/glossary.html#uncovered-type), typically
a [unit struct](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#unit-like-structs-without-any-fields). 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 for Bar` to
implement the conversion.

```rust
pub fn foo(value: impl Into) { ... }
```

## Alternatives

### Newtype

Typically, a
local [newtype](https://doc.rust-lang.org/book/ch20-02-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-typesl)
is used to implement the standard library's `From` and `TryFrom` traits when both types are non-local.

Either:

```rust
struct FooWrapper(Foo);

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

Or:

```rust
struct BarWrapper(Bar);

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

### Conversion function

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

```rust
fn convert(_value: Foo) -> Bar {
Bar
}
fn try_convert(_value: Foo) -> Result {
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](LICENSE) for details.

Copyright 2025