Was ist ein „grundlegender Typ“ in Rust?

37

Irgendwo habe ich den Begriff "Grundtyp" (und sein Attribut #[fundamental]) aufgegriffen und wollte gerade mehr darüber erfahren. Ich erinnere mich vage, dass es in einigen Situationen darum ging, die Kohärenzregeln zu lockern. Und ich denke, die Referenztypen sind solche grundlegenden Typen.

Leider hat mich das Durchsuchen des Webs nicht sehr weit gebracht. Die Rust-Referenz erwähnt es nicht (soweit ich sehen kann). Ich habe gerade ein Problem mit der Erstellung grundlegender Tupeltypen und dem RFC gefunden, mit dem das Attribut eingeführt wurde . Der RFC enthält jedoch einen einzigen Absatz über grundlegende Typen:

  • Ein #[fundamental]Typ Fooist einer, bei dem das Implementieren eines Blanket-Impl-Over Fooeine bahnbrechende Änderung darstellt. Wie beschrieben &und &mutsind grundlegend. Dieses Attribut würde angewendet Box, so dass Box verhalten sich gleich wie &und &mutin Bezug auf Kohärenz.

Ich finde den Wortlaut ziemlich schwer zu verstehen und es scheint, als ob ich gründliche Kenntnisse des vollständigen RFC benötige, um dieses Bit über grundlegende Typen zu verstehen. Ich hatte gehofft, jemand könnte grundlegende Typen in etwas einfacheren Begriffen erklären (natürlich ohne zu viel zu vereinfachen). Diese Frage würde auch als leicht zu findendes Wissen dienen.

Um grundlegende Typen zu verstehen, möchte ich diese Fragen beantworten (zusätzlich zu der Hauptfrage "Was sind sie überhaupt?" Natürlich):

  • Können fundamentale Typen mehr als nicht fundamentale?
  • Kann ich als Bibliotheksautor in irgendeiner Weise davon profitieren, einige meiner Typen als zu #[fundamental]kennzeichnen?
  • Welche Typen aus der Kernsprache oder der Standardbibliothek sind grundlegend?
Lukas Kalbertodt
quelle

Antworten:

34

Wenn eine Bibliothek einen generischen Typ hat Foo<T>, können nachgeschaltete Kisten normalerweise keine Merkmale darauf implementieren, selbst wenn Tes sich um einen lokalen Typ handelt. Zum Beispiel,

( 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)
    }
}

Für ein konkretes Beispiel, das auf dem Spielplatz funktioniert (dh einen Fehler ergibt),

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

(Spielplatz)


Dies ermöglicht es dem Kistenautor normalerweise, (pauschale) Implementierungen von Merkmalen hinzuzufügen, ohne nachgeschaltete Kisten zu zerbrechen. Das ist großartig in Fällen, in denen zunächst nicht sicher ist, ob ein Typ ein bestimmtes Merkmal implementieren soll, später jedoch klar wird, dass dies der Fall sein sollte. Zum Beispiel könnten wir einen numerischen Typ haben, der die Merkmale von zunächst nicht implementiert num-traits. Diese Eigenschaften könnten später hinzugefügt werden, ohne dass eine Änderung erforderlich wäre.

In einigen Fällen möchte der Bibliotheksautor jedoch, dass nachgeschaltete Kisten Merkmale selbst implementieren können. Hier kommt das #[fundamental]Attribut ins Spiel. Wenn es auf einen Typ gesetzt wird, wird jedes Merkmal, das derzeit nicht für diesen Typ implementiert ist, nicht implementiert (außer bei einer Änderung). Infolgedessen können nachgeschaltete Kisten Merkmale für diesen Typ implementieren, solange ein Typparameter lokal ist (es gibt einige komplizierte Regeln für die Entscheidung, welche Typparameter dafür zählen). Da der Grundtyp ein bestimmtes Merkmal nicht implementiert, kann dieses Merkmal frei implementiert werden, ohne Kohärenzprobleme zu verursachen.

Ist beispielsweise Box<T>markiert #[fundamental], damit der folgende Code (ähnlich der Rc<T>obigen Version) funktioniert. Box<T>nicht implementiert Default(außer Timplementiert Default), so dass wir davon ausgehen können, dass dies in Zukunft nicht der Fall sein wird, da dies von Box<T>grundlegender Bedeutung ist. Beachten Sie, dass die Implementierung Defaultfür BarProbleme verursachen würde, da diese Box<Bar>bereits implementiert sind Default.

struct Bar;

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

(Spielplatz)


Andererseits können Merkmale auch mit markiert werden #[fundamental]. Dies hat eine doppelte Bedeutung für grundlegende Typen. Wenn ein Typ derzeit kein grundlegendes Merkmal implementiert, kann davon ausgegangen werden, dass dieser Typ es in Zukunft nicht implementiert (wiederum ohne eine Änderung). Ich bin mir nicht ganz sicher, wie dies in der Praxis angewendet wird. Im Code (unten verlinkt) FnMutist er mit dem Hinweis markiert, dass er für Regex benötigt wird (etwas über &str: !FnMut). Ich konnte nicht finden, wo es in der regexKiste verwendet wird oder ob es woanders verwendet wird.

Wenn das AddMerkmal als grundlegend markiert wäre (was diskutiert wurde), könnte dies theoretisch verwendet werden, um eine Addition zwischen Dingen zu implementieren, die es noch nicht haben. Zum Beispiel das Hinzufügen [MyNumericType; 3](punktuell), was in bestimmten Situationen nützlich sein könnte (natürlich [T; N]würde es auch möglich sein , grundlegend zu machen).


Die primitiven Grundtypen sind &T, &mut T(siehe hier für eine Demonstration aller Generika primitive Typen). In der Standardbibliothek Box<T>und Pin<T>sind auch als grundlegend gekennzeichnet.

Die Grundzüge in der Standardbibliothek sind Sized, Fn<T>, FnMut<T>, FnOnce<T>und Generator.


Beachten Sie, dass das #[fundamental]Attribut derzeit instabil ist. Das Tracking-Problem ist das Problem Nr. 29635 .

SCappella
quelle
1
Gute Antwort! In Bezug auf primitive Typen: Es gibt nur eine Handvoll generische Urtyp: &T, &mut T, *const T, *mut T, [T; N], [T], fnZeiger und Tupel. Und wenn Sie alle testen (bitte sagen Sie mir, wenn dieser Code keinen Sinn ergibt) , scheinen Referenzen die einzigen grundlegenden primitiven Typen zu sein . Interessant. Mich würde interessieren, warum die anderen es nicht sind, insbesondere rohe Hinweise. Aber das ist wohl nicht der Umfang dieser Frage.
Lukas Kalbertodt
1
@LukasKalbertodt Danke für die Informationen zu primitiven Typen. Ich habe in Ihren Tests hinzugefügt. Informationen zu Referenzen und Zeigern finden Sie in diesem Kommentar in der RFC-Pull-Anforderung.
SCappella
Die Referenz dokumentiert keine instabilen Attribute, deshalb haben Sie sie dort nicht gefunden.
Havvy