Перейти к содержанию

Случай стабилизации произвольных типов self_types

Posted on:5 мая 2023 г. at 09:22

Example Dynamic OG Image link

В нестабильном Rust уже несколько лет есть функция arbitrary_self_types. Давайте стабилизируем это!

Во-первых, что это за функция?

В обычном Rust мы можем вызывать методы для типа, даже если у нас не совсем есть тип, но вместо этого (скажем) Rc тип

struct MyThingy;

impl MyThingy {
	fn say_hello(&self) {
		println!("Hello!");
	}
}

fn main() {
	let thing = std::rc::Rc::new(MyThingy);
	thing.say_hello();
}

Это также работает, если получатель метода (тип self) является интеллектуальным указателем, таким как Rc :

struct MyThingy;

impl MyThingy {
	fn say_hello(self: std::rc::Rc<Self>) {
		println!("Hello!");
	}
}

fn main() {
	let thing = std::rc::Rc::new(MyThingy);
	thing.say_hello();
}

(Обратите внимание, что мы добавляем методы в блок impl для MyThingy, а не для Rc, даже если тип принимающей стороны Rc). Это работает для различных видов “умных указателей”, включая:

Но они жестко запрограммированы. Ты не можешь этого сделать:

struct MySmartRc<T>(T); // в действительности это будет куча
						// количество ссылок или подобное

impl<T> MySmartRc<T> {
	fn new(t: T) -> Self {
		Self(t)
	}
}

struct MyThingy;

impl MyThingy {

	fn say_hello(self: MySmartRc<Self>) {
		println!("Hello!");
	}
}

impl<T> std::ops::Deref for MySmartRc<T> {
	type Target = T;
	fn deref(&self) -> &Self::Target {
		&self.0
	}
}

fn main() {
	let thing = MySmartRc::new(MyThingy);
	thing.say_hello(); // не компилируется!
}

За исключением того, что вы можете скомпилировать, если используете arbitrary_self_types . Подобный этому:

#![feature(arbitrary_self_types)]

struct MySmartRc<T>(T); // в действительности это будет куча
						// количество ссылок или подобное

impl<T> MySmartRc<T> {
	fn new(t: T) -> Self {
		Self(t)
	}
}

struct MyThingy;

impl MyThingy {
	fn say_hello(self: MySmartRc<Self>) {
		println!("Hello!");
	}
}

impl<T> std::ops::Deref for MySmartRc<T> {
	type Target = T;
	fn deref(&self) -> &Self::Target {
		&self.0
	}
}

fn main() {
	let thing = MySmartRc::new(MyThingy);
	thing.say_hello(); // снова компилирует
}

Так что, в общем-то, все. arbitrary_self_types - это способ расширить разрешение методов Rust, чтобы справиться с косвенностью с помощью пользовательских интеллектуальных указателей. Это работает для всего, что реализует признак Deref. (Были бы возможны и другие реализации, но, похоже, на данный момент нет никаких существенных причин предпочесть другую реализацию.)

Почему это полезно?

Во-первых, кажется плохим, что этот поиск по методу жестко запрограммирован для Box, Rc и т.д. Когда Rust даже позволяет вам заменить асинхронный исполнитель, кажется, что жестко кодировать такие вещи противоречит нормам Rust. (Технически они не совсем жестко закодированы, они идентифицируются с помощью признака lang_item Receiver, который не предназначен для использования вне стандартной библиотеки Rust, так что это почти то же самое, что и жесткое кодирование.)

Но как насчет конкретных вариантов использования? Вот некоторые из них.

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