Могу ли я объявить, что реализация данного признака не должна быть рассчитана на Size? - PullRequest
0 голосов
/ 23 мая 2018

Я определил черту Foo, реализовал эту черту для [u32] и написал функцию bar, принимающую эту черту в качестве аргумента ( детская площадка ):

trait Foo {
    fn foo(&self) -> u32;
}

impl Foo for [u32] {
    fn foo(&self) -> u32 {
        self[0]
    }
}

fn bar<T>(f: &T) -> u32
where
    T: Foo,
{
    f.foo() + 1
}

fn main() {
    let f: &[u32] = &[42];
    bar(f);
}

Это не компилируется, потому что bar неявно ожидает, что его аргументы будут Sized:

error[E0277]: the trait bound `[u32]: std::marker::Sized` is not satisfied
  --> src/main.rs:20:5
   |
20 |     bar(f);
   |     ^^^ `[u32]` does not have a constant size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `[u32]`
note: required by `bar`
  --> src/main.rs:11:1
   |
11 | / fn bar<T>(f: &T) -> u32
12 | | where
13 | |     T: Foo,
14 | | {
15 | |     f.foo() + 1
16 | | }
   | |_^

Я могу исправить это с помощью T: Foo + ?Sized, но тогда мне придется сделать это для каждые функция, ожидающая Foo, что вызывает боль ...

Можно ли раз и навсегда объявить, что реализации Foo не следует ожидать Sized?Я попытался trait Foo: ?Sized в строке 1, но компилятор жалуется на это.

Этот вопрос не совпадает с Trait, реализующим Sized .В этом вопросе параметр Foo перемещается, поэтому обычно компилятор хочет знать его размер во время компиляции.В моем случае параметр является ссылкой, поэтому ему не требуется размер , но все же компилятор неявно предполагает, что это так, если явно не указано (с использованием + ?Sized).Что бы я хотел изменить, так это неявное предположение, касающееся этой конкретной черты.

1 Ответ

0 голосов
/ 23 мая 2018

Почему компилятор жалуется на trait Foo: ?Sized?

Когда вы говорите, что каждый Foo равен Sized, вы как бы скрываете правду для себя.Да, каждый Foo равен Sized, но на самом деле каждый тип имеет определенный размер в некоторой точке.Действительно важная информация заключается в том, что вы не говорите, сколько этот размер.

В этом случае вы просите размер [u32]:

error[E0277]: the trait bound `[u32]: std::marker::Sized` is not satisfied
  |
  | impl Foo for [u32] {
  |      ^^^ `[u32]` does not have a constant size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `[u32]`

Для получения дополнительной информацииобратитесь к этому ответу из Черта, реализующая Sized .

Почему T не может быть безразмерным, поскольку у нас есть только ссылка?

Позвольте мне еще раз процитировать этот ответ из Почему ссылка на признак в универсальной функции должна реализовывать Sized?

По умолчанию все универсальные типы для функций неявно имеют ограничение Sized, независимо от того, как они используются.Вам необходимо явно отказаться от этого требования, используя ?Sized

Это решит вашу проблему:

fn bar<T>(f: &T) -> u32
where
    T: Foo + ?Sized,
{
    f.foo() + 1
}

Детская площадка

Также было бы возможно реализовать Foo для &[u32]:

impl<'a> Foo for &'a [u32] {
    fn foo(&self) -> u32 {
        self[0]
    }
}

fn bar<T>(f: T) -> u32
where
    T: Foo,
{
    f.foo() + 1
}

Playground

В этом конкретном случае вы могли бы даже обобщить вашу реализацию дляFoo и будет работать с Vec s и массивами, а также ссылками на эти типы:

impl<T: AsRef<[u32]>> Foo for T {
    fn foo(&self) -> u32 {
        self.as_ref()[0]
    }
}

С новым синтаксисом impl Trait , bar может быть сокращенов последних двух случаях

fn bar(f: impl Foo) -> u32 {
    f.foo() + 1
}

Детская площадка

...