2

Ferris Talk #10: Constant Fun mit Rust – const fn

 1 year ago
source link: https://www.heise.de/hintergrund/Ferris-Talk-10-Constant-Fun-mit-Rust-const-fn-7162074.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Ferris Talk #10: Constant Fun mit Rust – const fn

In Version 1.61 hat Rust Tricks in Sachen konstanter Code-Evaluierung gelernt. Diese Ausgabe der Kolumne zeigt, was const in Rust kann und was es Neues gibt.

Ferris Talk – Neuigkeiten zu Rust. Eine Heise-Kolumne von Rainer Stropek und Stefan Baumgartner für Rustaceans

05.07.2022

08:06 Uhr

Inhaltsverzeichnis

Egal, von welcher Programmiersprache man kommt, Konstanten gibt es praktisch überall. Rust wäre nicht seit Jahren eine der beliebtesten Programmiersprachen laut Stack Overflow Survey, wenn sie nicht auch im Bereich der Konstanten etwas zu bieten hätte, was man in vielen anderen Sprachen nicht findet. In der kürzlich erschienenen Version 1.61 hat Rust einige Tricks in Sachen Konstanten und konstanter Evaluierung von Code dazugelernt.

Die in nächster Zeit geplanten Versionen werden laut aktuell verfügbarer Nightly weitere Neuerungen in dem Bereich bringen. Das nehmen wir in der Rust-Kolumne zum Anlass, in dieser Ausgabe des Ferris Talk const und const fn in Rust vorzustellen und anhand von Beispielen zu zeigen, was es Neues gibt.

Ferris Talks – die Kolumne für Rustaceans

In diesem Artikel stehen die Codebeispiele im Mittelpunkt. Damit Interessierte den gezeigten Code ausprobieren und vor allem damit experimentieren können, ist bei jedem Beispiel ein Link angegeben, der den Code im Rust Playground öffnet. Dort lässt er sich ausführen und ändern, selbst wenn Rust noch nicht lokal installiert sein sollte.

Grundlagen zu Konstanten in Rust

Starten wir mit den Grundlagen, die in Rust ähnlich funktionieren wie in anderen Programmiersprachen. Folgender Code legt Konstanten mit verschiedenen Typen an (siehe Rust Playground, Sample 1). Die Zuweisung des Wertes zur Konstanten ANSWER ist besonders zu beachten. Der Wert lässt sich durch einen konstanten Ausdruck ermitteln. In einem solchen Kontext steht nicht alles zur Verfügung, was Rust zur Laufzeit an Ausdrücken beherrscht. Die konkreten Einschränkungen sind in der Rust-Dokumentation beschrieben.

// A simple constant value
const NEARLY_THE_ANSWER: i32 = 41;

// A constant expression that is evaluated at compile time.
// Note: Not everything is allowed in a constant expression. See
// https://doc.rust-lang.org/reference/const_eval.html#constant-expressions
// for details
const ANSWER: i32 = NEARLY_THE_ANSWER + 1;

// A simple constant string (because of static lifetime elision,
// we don't need 'static)
const TEXT: &str = "the quick brown fox jumps over the lazy dog!";

// A constant array
const NUMBERS: [i32; 5] = [1, 2, 3, 4, 5];

fn main() {
    // Constants are inlined at compile time wherever they are used.
    println!("The answer is {ANSWER}");
    println!("{TEXT}");
    println!("Numbers: {:?}", NUMBERS);

    // Constants can be declared in any scope, not just global.
    const VERSION: &str = "1.2.3";
    println!("Version: {}", VERSION);
}

Konstanten sind strukturierte Datentypen

Konstanten in Rust sind nicht auf Basisdatentypen wie Zahlen oder Zeichenketten beschränkt. Auch Strukturen können const sein, wie das folgende Codebeispiel zeigt (siehe auch Rust Playground, Sample 2).

// Constants in Rust don't need to be basic data types.
// Structs can also be constant.
#[derive(Debug)]
struct Customer<'a> {
    name: &'a str,
    age: i32,
}
const CUSTOMER: Customer = Customer {
    name: "John",
    age: 42,
};

fn main() {
    println!(
        "Customer {} is of age {} ({:?})",
        CUSTOMER.name, CUSTOMER.age, CUSTOMER
    );

    // Note that if you modify a const item, a new temporary
    // item is created. The original const item is not modified.
    CUSTOMER.age += 1;
}

In Rust können Konstanten auf die Adressen anderer Konstanten verweisen. Das folgende Beispiel demonstriert das, indem es eine Struktur NamedNumbers definiert und im Rahmen einer Konstante verwendet (siehe Rust Playground, Sample 3). Die Konstante verweist auf andere Konstanten. Zu erwähnen ist dabei, dass beim Verwenden der Struktur im Rahmen der Konstantendeklaration die static Lifetime nicht explizit anzugeben ist, Rust ergänzt sie im Hintergrund automatisch.

const TEXT: &str = "the quick brown fox jumps over the lazy dog!";
const NUMBERS: [i32; 5] = [1, 2, 3, 4, 5];

// A constant struct.
// Note that constants may refer to the address of other constants.
// The lifetime defaults to 'static (elided).
struct NamedNumbers<'a> {
    name: &'a str,
    numbers: &'a [i32; 5],
}
const NAMED_NUMBERS: NamedNumbers = NamedNumbers {
    name: TEXT,
    numbers: &NUMBERS,
};

fn main() {
    println!("{TEXT:p}\n{:p}", NAMED_NUMBERS.name);
    println!("{}", NAMED_NUMBERS.numbers.iter().sum::<i32>());
}

Konstanten in Traits

Die nächste Besonderheit von Rust ist, dass Konstanten mit Typen assoziiert werden und sogar Teil eines Traits sein können. Im folgenden Beispiel verlangt der Trait HasNumbers, dass zwei Konstanten existieren (siehe Rust Playground, Sample 4). Für eine davon ist ein überschreibbarer Standardwert vorzugeben.

Constant fun (const fn) with Rust – Rainer Stropek | Rust Meetup Linz, Juni 2022
// Constants in traits
trait HasNumbers {
    // Note that we have a constant here without a value.
    const NUMBERS: [i32; 5];

    // Constants in traits can have default values
    const LAST_NUMBER: i32 = 5;
}
struct IHaveNumbers {}
impl HasNumbers for IHaveNumbers {
    // This is an *associated constant*, as it is associated with a type.
    const NUMBERS: [i32; 5] = [1, 2, 3, 4, IHaveNumbers::LAST_NUMBER];
}
struct IHaveOtherNumbers {}
impl HasNumbers for IHaveOtherNumbers {
    // Here we override the default value of the trait.
    const LAST_NUMBER: i32 = 6;
    const NUMBERS: [i32; 5] = [1, 2, 3, 4, IHaveOtherNumbers::LAST_NUMBER];
}

fn main() {
    println!("{:?}", IHaveNumbers::NUMBERS);
    println!("{:?}", IHaveOtherNumbers::NUMBERS);
}

Konstanten mit Destruktoren

Jetzt verlassen wir denjenigen Bereich der Konstanten, der in anderen Programmiersprachen ähnlich ist, und sprechen über weiterführende Fähigkeiten von Rust. Als Erstes geht es um konstante Ausdrücke mit Destruktoren. Ja, richtig gehört: Konstante Ausdrücken können in Rust Destruktoren haben, die an der entsprechenden Stelle im Programm ausgeführt werden, wenn die Konstante verwendet wird. Etwas anders ist das bei statischen Variablen. Ihr Destruktor wird nicht aufgerufen, wenn das Programm bzw. der Thread beendet wird. Das folgende Listing (siehe auch Rust Playground, Sample 5) demonstriert dieses Verhalten.

// Constants can have destructors
struct WillSayGoodbye<'a>(&'a str);
impl<'a> Drop for WillSayGoodbye<'a> {
    fn drop(&mut self) {
        println!("{}", self.0);
    }
}
// Note that destructor on statics will not run on program/thread exit.
static _GOODBYE_IN_ENGLISH: WillSayGoodbye = WillSayGoodbye("Goodbye");
const GOODBYE_IN_GERMAN: WillSayGoodbye = WillSayGoodbye("Auf Wiedersehen");

fn main() {
    {
        let _goodbye_sayer = GOODBYE_IN_GERMAN;
        // Destructor will run at appropriate point when const is used. Therefore,
        // this code will print "Auf Wiedersehen" on stdout when var goes out of scope.    }
}
Seiten

Auf einer Seite lesen comments_outline_white Beitrag kommentieren

Zur Startseite


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK