12
votes

My goal is to make the last 2 lines of this code compile and the last assertion to pass:

struct State {
    string: String
}

impl State {
    fn string<F: FnMut(String) -> String>(mut self, mut f: F) -> Self {
        self.string = f(self.string);
        self
    }
}

fn main() {
    let state = State { string: String::from("foo") };
    assert_eq!(state.string, "foo");
    let state = state.string(|old| old + "bar");
    assert_eq!(state.string, "foobar");
    // let state = state.string(String::from("baz"));
    // assert_eq!(state.string, "baz");
}

I thought this would be possible with traits and specialization, but the following code:

#![feature(specialization)]

trait Get<T> {
    fn get(self, old: T) -> T;
}

impl<T> Get<T> for T {
    default fn get(self, _: T) -> T {
        self
    }
}

impl<T, F> Get<T> for F where F: FnMut(T) -> T {
    fn get(mut self, old: T) -> T {
        self(old)
    }
}

struct State {
    string: String
}

impl State {
    fn string<G: Get<String>>(mut self, g: G) -> Self {
        self.string = g.get(self.string);
        self
    }
}

throws this error (live):

error[E0119]: conflicting implementations of trait `Get<_>`:
  --> <anon>:13:1
   |
13 | impl<T, F> Get<T> for F where F: FnMut(T) -> T {
   | ^
   |
note: conflicting implementation is here:
  --> <anon>:7:1
   |
7  | impl<T> Get<T> for T {
   | ^

error: aborting due to previous error

So my question is, why is the second impl of Get not more "specific" than the first one, and is there any way in current stable or nightly Rust to get my original code to work?

Edit: I know implementing a trait for just one type would work, but I want a generic solution for any type as I want to be able to use this for any arbitrary fields of a struct.

2
What if T implements FnMut(T) -> T?CodesInChaos
@CodesInChaos I would expect specialization to choose the FnMut(T) -> T implementation but I'll be happy with either one as I'm not going to be using it with a type of that kind.Dogbert

2 Answers

6
votes

For your concrete issue, you don't need specialization:

struct State {
    string: String,
}

impl State {
    fn string<F>(mut self, mut f: F) -> Self
        where F: Thing
    {
        self.string = f.thing(self.string);
        self
    }
}

trait Thing {
    fn thing(&mut self, s: String) -> String;
}

impl Thing for String {
    fn thing(&mut self, _s: String) -> String {
        self.clone()
    }
}

impl<F> Thing for F
    where F: FnMut(String) -> String
{
    fn thing(&mut self, s: String) -> String {
        (self)(s)
    }
}

fn main() {
    let state = State { string: String::from("foo") };
    assert_eq!(state.string, "foo");
    let state = state.string(|old| old + "bar");
    assert_eq!(state.string, "foobar");
    let state = state.string(String::from("baz"));
    assert_eq!(state.string, "baz");
}

You may either want to require FnOnce or implement the trait for a &str. Right now, the allocation of the String is not being used, causing a bit of inefficiency.

You could then implement the trait multiple times for the interesting types:

struct State {
    string: String,
    vec: Vec<u8>,
}

impl State {
    fn string<F>(mut self, mut f: F) -> Self
        where F: Thing<String>
    {
        self.string = f.thing(self.string);
        self
    }

    fn vec<F>(mut self, mut f: F) -> Self
        where F: Thing<Vec<u8>>
    {
        self.vec = f.thing(self.vec);
        self
    }
}

trait Thing<T> {
    fn thing(&mut self, s: T) -> T;
}

impl Thing<String> for String {
    fn thing(&mut self, _s: String) -> String {
        self.clone()
    }
}

impl<F> Thing<String> for F
    where F: FnMut(String) -> String
{
    fn thing(&mut self, s: String) -> String {
        (self)(s)
    }
}

impl Thing<Vec<u8>> for Vec<u8> {
    fn thing(&mut self, _s: Vec<u8>) -> Vec<u8> {
        self.clone()
    }
}

impl<F> Thing<Vec<u8>> for F
    where F: FnMut(Vec<u8>) -> Vec<u8>
{
    fn thing(&mut self, s: Vec<u8>) -> Vec<u8> {
        (self)(s)
    }
}

fn main() {
    let state = State { string: String::from("foo"), vec: vec![1] };

    assert_eq!(state.string, "foo");
    let state = state.string(|old| old + "bar");
    assert_eq!(state.string, "foobar");
    let state = state.string(String::from("baz"));
    assert_eq!(state.string, "baz");

    assert_eq!(state.vec, [1]);
    let state = state.vec(|mut old: Vec<u8>| {
        old.push(2);
        old
    });
    assert_eq!(state.vec, [1, 2]);
    let state = state.vec(vec![3]);
    assert_eq!(state.vec, [3]);
}

I believe that repetition could be handled by a macro:

macro_rules! thing {
    ($t: ty) => {
        impl Thing<$t> for $t {
            default fn thing(&mut self, _val: $t) -> $t {
                self.clone()
            }
        }

        impl<F> Thing<$t> for F
            where F: FnMut($t) -> $t
        {
            fn thing(&mut self, val: $t) -> $t {
                (self)(val)
            }
        }
    }
}

thing!(String);
thing!(Vec<u8>);
4
votes

Specialization doesn't work here because specialization only works for chains. That is, there exist functions that satisfy the impl

impl<T, F> Get<T> for F where F: FnMut(T) -> T

but not

impl<T> Get<T> for T

so the latter cannot specialize the former.

The simplest way to fix this is to just write a GetString trait instead of a Get<T> trait; that way you don't have to consider specialization on such malarkey at all.