2016-11-01 8 views
4

При реализации признака мы часто используем ключевое слово self, образец выглядит следующим образом. Я хочу понять представление многих применений self в этом примере кода.Понимание параметра «self» в контексте реализации признаков

struct Circle { 
    x: f64, 
    y: f64, 
    radius: f64, 
} 

trait HasArea { 
    fn area(&self) -> f64;   // first self: &self is equivalent to &HasArea 
} 

impl HasArea for Circle { 
    fn area(&self) -> f64 {   //second self: &self is equivalent to &Circle 
     std::f64::consts::PI * (self.radius * self.radius) // third:self 
    } 
} 

Мое понимание:

  1. Первый self: &self эквивалентно &HasArea.
  2. Второй self: &self эквивалентен &Circle.
  3. Является третьим self, представляющим Circle? Если да, то если self.radius использовался дважды, это вызовет проблему перемещения?

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

+1

Возможно, вас заинтересует [В чем разница между 'self' и' Self'] (http://stackoverflow.com/questions/32304595/whats-the-difference-between-self-and-self). –

+1

Вопрос о 'self.radius' немного отличается от вопроса о себе (речь идет о собственности и' Copy'). Я предлагаю вам отредактировать его, чтобы сосредоточиться на том, что «сам» здесь, так как это самая сложная (и новая) часть, тогда как у нас уже много вопросов о собственности. –

ответ

5

Вы в основном правы.

Как я думаю об этом, что в подписи метода, self является сокращенным:

impl S { 
    fn foo(self) {}  // equivalent to fn foo(self: S) 
    fn foo(&self) {}  // equivalent to fn foo(self: &S) 
    fn foo(&mut self) {} // equivalent to fn foo(self: &mut S) 
} 

Это не фактически эквивалентны, так как self это ключевое слово, и есть некоторые специальные правила (например, для пожизненная элита), но это довольно близко.

Назад к вашему примеру:

impl HasArea for Circle { 
    fn area(&self) -> f64 { // like fn area(self: &Circle) -> ... 
     std::f64::consts::PI * (self.radius * self.radius) 
    } 
} 

self в организме типа &Circle. Вы не можете отойти от ссылки, так что self.radius не может двигаться даже один раз. В этом случае radius реализует Copy, поэтому он просто скопирован, а не перемещен. Если бы это был более сложный тип, который не реализовал Copy, это было бы ошибкой.

+0

Итак, self.radius похож на Circle.radius, так как радиус f64, поэтому он реализует свойство Copy. Верный? – enaJ

+1

Да, 'self' является ссылкой на' Circle', так как это метод в 'Circle', поэтому' self.radius' является 'f64'. –

4

Вы в основном верны.


Существует ловкий трюк, чтобы компилятор сказать вам тип переменных, а не пытаться вывести их: let() = ...;.

Использование the Playground я за 1-й случай:

9 |   let() = self; 
    |    ^^ expected &Self, found() 

и 2-й случай:

16 |   let() = self; 
    |    ^^ expected &Circle, found() 

Первый случай действительно особенным, потому что HasArea не является типом, это черта.

И что такое self? Это ничего еще.

Сказал иначе, он представляет для любой возможный конкретный тип, который может реализовать HasArea. И, таким образом, единственная гарантия, которую мы имеем об этой особенности, заключается в том, что она обеспечивает не менее интерфейс HasArea.

Главное, что вы можете разместить дополнительные ограничения. Например, вы могли бы сказать:

trait HasArea: Debug { 
    fn area(&self) -> f64; 
} 

И в этом случае, Self: HasArea + Debug, что означает, что self обеспечивает как интерфейсы HasArea и Debug.


Вторые и третьи случаи гораздо проще: мы знаем, типточного бетона, для которого реализовано HasArea черты. Это Circle.

Таким образом, тип self в методе fn area(&self) составляет &Circle.

Обратите внимание, что если тип параметра равен &Circle, то из этого следует, что во всех его применениях в методе это &Circle. Ржавчина имеет статическую типизацию (и не зависящую от потока ввода), поэтому тип привязки не меняется во время его жизни.


Однако все может усложниться.

Представьте, что у вас есть две черты:

struct Segment(Point, Point); 

impl Segment { 
    fn length(&self) -> f64; 
} 

trait Segmentify { 
    fn segmentify(&self) -> Vec<Segment>; 
} 

trait HasPerimeter { 
    fn has_perimeter(&self) -> f64; 
} 

Затем, вы можете реализовать HasPerimeter автоматически для всех форм, которые могут быть разбиты в последовательности сегментов.

impl<T> HasPerimeter for T 
    where T: Segmentify 
{ 
    // Note: there is a "functional" implementation if you prefer 
    fn has_perimeter(&self) -> f64 { 
     let mut total = 0.0; 
     for s in self.segmentify() { total += s.length(); } 
     total 
    } 
} 

Какое здесь значение self? Это &T.

Что такое T? Любой тип, который реализует Segmentify.

И поэтому все, что мы знаем о T, что она реализует SegmentifyHasPerimeter и, и больше ничего (мы не могли использовать println("{:?", self); потому T не гарантируется для реализации Debug).

+0

"Обратите внимание, что если тип параметра равен & Circle, то это означает, что во всех его применениях в методе это & ​​Circle.Ржавчина имеет статическую типизацию (и не зависящую от потока ввода), поэтому тип привязки не меняется во время его жизни ». Итак, self.radius похож на & Circle.radius? – enaJ

+1

@enaJ:' self.radius' is ' f64' yes, который является типом 'Copy'. –