Подтвердить что ты не робот

Разрешить функции принимать `T` или любой` FnMut (T) → T`

Моя цель состоит в том, чтобы скомпилировать последние 2 строки этого кода и передать последнее утверждение:

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");
}

Я думал, что это будет возможно с особенностями и специализацией, но следующий код:

#![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
    }
}

вызывает эту ошибку (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

Итак, мой вопрос: почему второй вариант Get не более "специфичен", чем первый, и есть ли какой-либо способ в текущей стабильной или ночной Rust, чтобы заставить мой исходный код работать?

Изменить: я знаю, что реализация признака только для одного типа будет работать, но я хочу использовать общее решение для любого типа, поскольку я хочу использовать его для любых произвольных полей структуры.

4b9b3361

Ответ 1

Для конкретной проблемы вам не нужна специализация:

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");
}

Вы можете либо потребовать FnOnce, либо реализовать признак для &str. Прямо сейчас выделение String не используется, что приводит к небольшой неэффективности.

Затем вы могли бы реализовать признак несколько раз для интересных типов:

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]);
}

Я считаю, что повторение может выполняться макросом:

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>);

Ответ 2

Специализация здесь не работает, потому что специализация работает только для цепей. То есть существуют функции, удовлетворяющие условию impl

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

но не

impl<T> Get<T> for T

поэтому последний не может специализировать первый.

Самый простой способ исправить это - просто написать черту GetString вместо тэга Get<T>; таким образом, вам не нужно вообще заниматься специализацией по такому malarkey.