Що таке "фундаментальний тип" у Русті?


37

Десь я підібрав термін "фундаментальний тип" (і його атрибут #[fundamental]) і просто зараз хотів дізнатися про нього більше. Я смутно пам'ятаю, що це стосується послаблення правил узгодженості в деяких ситуаціях. І я думаю, що базові типи є такими фундаментальними.

На жаль, пошук в Інтернеті не привів мене дуже далеко. Посилання на Руст не згадує (наскільки я бачу). Я щойно знайшов проблему щодо створення кортежів основних типів та RFC, який запровадив атрибут . Однак у RFC є єдиний параграф про основні типи:

  • #[fundamental]Тип Fooє один , де реалізується ковдру осущ через Fooце розривний зміна. Як описано, &і &mutє основоположними. Цей атрибут буде застосований до Box, змушуючи Box себе поводитись так само, як &і &mutщодо узгодженості.

Я вважаю формулювання досить важким для розуміння, і мені здається, що мені потрібні глибокі знання повної RFC, щоб зрозуміти цей біт про основні типи. Я сподівався, що хтось може пояснити основні типи дещо простішими термінами (звичайно, не надто спрощуючи). Це питання також послужило б легким для пошуку частиною знань.

Щоб зрозуміти фундаментальні типи, я хотів би відповісти на ці запитання (крім основного питання "на що вони рівні?"):

  • Чи можуть фундаментальні типи зробити більше, ніж не фундаментальні?
  • Чи можу я, як автор бібліотеки, певним чином отримати користь від позначення деяких типів як #[fundamental]?
  • Які типи основної мови або стандартної бібліотеки є основними?

Відповіді:


34

Зазвичай, якщо бібліотека має загальний тип Foo<T>, ящики нижче за течією не можуть реалізувати на ній ознаки, навіть якщо Tце якийсь локальний тип. Наприклад,

( crate_a)

struct Foo<T>(pub t: T)

( crate_b)

use crate_a::Foo;

struct Bar;

// This causes an error
impl Clone for Foo<Bar> {
    fn clone(&self) -> Self {
        Foo(Bar)
    }
}

Для конкретного прикладу, який працює на дитячому майданчику (тобто дає помилку),

use std::rc::Rc;

struct Bar;

// This causes an error
// error[E0117]: only traits defined in the current crate
// can be implemented for arbitrary types
impl Default for Rc<Bar> {
    fn default() -> Self {
        Rc::new(Bar)
    }
}

(дитячий майданчик)


Це, як правило, дає змогу автору ящиків додавати (бланкетне) реалізацію ознак, не розбиваючи ящики вниз за течією. Це чудово у випадках, коли спочатку не визначено, що тип повинен реалізувати певну ознаку, але згодом стає зрозуміло, що він повинен. Наприклад, у нас може бути якийсь числовий тип, який спочатку не реалізує ознаки num-traits. Ці риси можна буде додати пізніше, не потребуючи перерви.

Однак у деяких випадках автор бібліотеки хоче, щоб ящики вниз за течією могли самі реалізувати риси. Тут розміщується #[fundamental]атрибут. Якщо розміщуватися на типі, будь-яка ознака, що не реалізована в даний час для цього типу, не буде реалізована (забороняючи переломну зміну). В результаті ящики нижче за течією можуть реалізовувати риси для цього типу, якщо параметр типу є локальним (є деякі складні правила для визначення того, які параметри типу для цього враховуються). Оскільки фундаментальний тип не реалізує задану ознаку, цю ознаку можна вільно реалізувати, не викликаючи проблем узгодженості.

Наприклад, Box<T>позначений #[fundamental], тому працює наступний код (схожий на Rc<T>версію вище). Box<T>не реалізовує Default(якщо не Tреалізує Default), тому ми можемо припустити, що це не буде в майбутньому, оскільки Box<T>є основним. Зверніть увагу , що реалізація Defaultдля Barвикличе проблеми, так як тоді Box<Bar>вже інвентарю Default.

struct Bar;

impl Default for Box<Bar> {
    fn default() -> Self {
        Box::new(Bar)
    }
}

(дитячий майданчик)


З іншого боку, ознаки також можна позначати #[fundamental]. Це має подвійне значення для фундаментальних типів. Якщо будь-який тип в даний час не реалізує основної ознаки, можна припустити, що цей тип не буде реалізовувати його в майбутньому (знову ж таки, забороняючи переломну зміну). Я не зовсім впевнений, як це використовується на практиці. У коді (зв'язаний нижче) FnMutпозначається основним із зауваженням, що він потрібен для регулярного вираження (щось про &str: !FnMut). Я не міг знайти, де він використовується в regexящику або якщо він використовується в іншому місці.

Теоретично, якби Addознака була позначена фундаментальною (про що йшлося), це можна було б використовувати для впровадження доповнення між речами, у яких її ще немає. Наприклад, додавання [MyNumericType; 3](в точку), що може бути корисним у певних ситуаціях (звичайно, [T; N]фундаментальне також дозволить це).


Примітивні основні типи &T, &mut T(див тут для демонстрації всіх загальних примітивних типів). У стандартній бібліотеці, Box<T>а Pin<T>також відзначені в якості основних.

Основні риси в стандартній бібліотеці Sized, Fn<T>, FnMut<T>, FnOnce<T>і Generator.


Зауважте, що #[fundamental]атрибут наразі нестабільний. Проблема відстеження - випуск №29635 .


1
Чудова відповідь! Що стосується примітивних типів: існує лише жменька родової примітивні типів: &T, &mut T, *const T, *mut T, [T; N], [T], fnпокажчик і кортежі. І протестуючи їх усіх (скажіть, будь ласка, якщо цей код не має сенсу), здається, що посилання є єдиними основними примітивними типами . Цікаво. Мені було б цікаво знати міркування, чому інші не є, особливо сировинні покажчики. Але я думаю, що це не обсяг цього питання.
Лукас Кальберттт

1
@LukasKalbertodt Дякую за інформацію про примітивні типи. Я додав у ваші тести. Що стосується обґрунтування щодо посилань та покажчиків, ознайомтесь із цим коментарем у запиті на тягу RFC
SCappella

Посилання не документує нестабільних атрибутів, тому ви його там не знайшли.
Havvy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.