web-dev-qa-db-fra.com

Comment puis-je rendre un élément Rust public dans une caisse, mais privé à l'extérieur?)

J'ai une caisse qui contient beaucoup de code, donc je l'ai divisée en plusieurs fichiers/modules. Cependant, certains modules ont des éléments internes dangereux (par exemple des pointeurs bruts) que je dois rendre publics aux différents modules, mais je ne veux pas les exposer aux utilisateurs de ma caisse. Comment puis je faire ça?

La seule façon dont je peux penser est que ma caisse ne soit qu'un gros module, mais il n'y a aucun moyen de le diviser en différents fichiers, à part cette solution qui semble un peu hacky.

Normalement, lorsque je rencontre un problème du monde réel que les exemples simples dans les documents Rust ne m'expliquent pas correctement, je copie simplement une caisse populaire réelle, par exemple git2-rs , mais cela semble simplement rendre tout public, y compris les pointeurs bruts.

24
Timmmm

Pour qu'un item soit exporté à partir d'une caisse de bibliothèque, il doit y avoir au moins un chemin menant à celui-ci dans lequel chaque est public. Cela signifie que tout ce dont vous avez besoin pour rendre un élément public dans votre caisse mais non exporté depuis la caisse (je l'appellerai désormais "interne", pour imiter la terminologie C #) est de le placer dans un module privé sous la racine de la caisse .

Cependant, cette solution est assez restrictive. Et si vous souhaitez avoir un module avec des fonctions exportées et des fonctions internes? Pour exporter certaines fonctions, nous devons rendre le module public, ce qui signifie que tous les éléments publics de ce module seront également exportés.

Depuis Rust 1.18 , il existe une solution adaptée à ce type de scénario: pub(restricted) . Cette fonctionnalité vous permet de spécifier le degré de public d'un élément. La syntaxe est assez flexible (vous pouvez rendre un élément visible à une arborescence de modules particulière au lieu de la caisse entière), mais si vous voulez rester simple, pub(crate) rendra un élément accessible n'importe où dans la caisse, mais pas à d'autres caisses (équivalent à internal en C #).

Par exemple, supposons que nous aimerions avoir un module util dans lequel foo est exporté (comme mycrate::util::foo), bar est interne et baz est privé au module. Le code pourrait ressembler à ceci:

pub mod util {
    pub fn foo() {
        unimplemented!()
    }

    pub(crate) fn bar() {
        unimplemented!()
    }

    fn baz() {
        unimplemented!()
    }
}

Si vous êtes bloqué sur Rust avant la version 1.18, il existe une solution de contournement, mais c'est un peu maladroit. Cela implique de définir tous vos éléments dans des modules privés et de réexporter uniquement ceux que vous souhaitez rendre publics (avec pub use) dans les modules publics qui seulement contiennent des réexportations. Voici à quoi ressemblerait l'exemple ci-dessus:

pub mod util {
    pub use util_impl::foo;
}

mod util_impl {
    pub fn foo() {
        unimplemented!()
    }

    pub fn bar() {
        unimplemented!()
    }

    fn baz() {
        unimplemented!()
    }
}

Non seulement ce n'est pas facile à lire et à comprendre, mais cela ne couvre pas toutes les situations où pub peut être utilisé. Par exemple, comment pourriez-vous rendre certains champs d'une structure exportée accessibles dans d'autres modules dans la même caisse sans les exporter également? La seule option serait d'exposer un wrapper avec un seul champ privé dont le type est la structure qui a des champs publics; cela fonctionne bien si vous souhaitez masquer tous les champs des autres caisses, mais pas si vous souhaitez exposer certains champs et rendre certains autres champs internes dans le même struct.

40
Francis Gagné