MINT lernen

Rust und Go: sicher, schnell

Zwei Textaufgaben mit Hinweisen und Erwartungshorizont zu Go und zu Ownership in Rust.

Dein Fortschritt:
0 / 0 Aufgaben
1

Die Kasse der Klassenfahrt

AFB I–II

Die Klasse 9c sammelt Geld für die Klassenfahrt. Pia hat in Go ein kleines Programm geschrieben, das die bisherigen Beiträge addiert. []int{25, 30, 25, 40} ist in Go eine Liste ganzer Zahlen, und for _, b := range beitraege geht die Liste Element für Element durch.

Go
package main

import "fmt"

func main() {
	beitraege := []int{25, 30, 25, 40}
	summe := 0
	for _, b := range beitraege {
		summe += b
	}
	fmt.Println("Summe:", summe)
}
  1. Nenne drei Stellen, an denen sich das Go-Programm in der Schreibweise von einem Java-Programm unterscheidet.
  2. Bestimme die Ausgabe des Programms.
  3. Pias Freund ergänzt die Zeile anzahl := len(beitraege), benutzt anzahl aber nirgends. Das Programm lässt sich danach nicht mehr übersetzen. Erläutere, warum Go hier so streng ist.

Hinweise

Hinweis zu Aufgabe a)
Achte auf die Variablendeklaration, den Programmrahmen, die Ausgabe und die Zeilenenden.
Hinweis zu Aufgabe b)
Addiere alle Elemente der Liste.
Hinweis zu Aufgabe c)
Überlege, was eine unbenutzte Variable über den Code verraten kann.

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)

Zum Beispiel: summe := 0 statt int summe = 0; (Typ wird erkannt); keine Semikolons am Zeilenende; func main() in package main statt public static void main in einer Klasse; Ausgabe mit fmt.Println statt System.out.println; keine Klammern um den Schleifenkopf.

Erwartungshorizont zu Aufgabe b)

25 + 30 + 25 + 40 = 120. Ausgabe: Summe: 120 (Println setzt zwischen die beiden Werte ein Leerzeichen).

Erwartungshorizont zu Aufgabe c)

In Go ist eine angelegte, aber nie benutzte Variable ein Compilerfehler. Eine solche Variable ist oft ein Zeichen für einen Denkfehler (man wollte sie benutzen und hat es vergessen) oder für überflüssigen Code. Go will möglichst wenige Sprachelemente und aufgeräumten, gut lesbaren Code — deshalb wird das nicht nur als Warnung gemeldet, sondern die Übersetzung verweigert.

2

Die Bibliotheks-App

AFB II–III

Die Schülerfirma entwickelt eine App für die Schulbibliothek. Noah testet ein Stück Rust-Code. Beim Übersetzen meldet der Compiler in der letzten Zeile von main: borrow of moved value: `titel`.

Rust
fn ausleihen(buch: String) {
    println!("Ausgeliehen: {}", buch);
}

fn anzeigen(buch: &String) {
    println!("Im Regal: {}", buch);
}

fn main() {
    let titel = String::from("Momo");
    anzeigen(&titel);
    ausleihen(titel);
    anzeigen(&titel);
}
Speicherbild während ausleihen(titel) läuft
main titel ungültig ausleihen buch Besitzer Besitz wandert (move) "Momo" Zeichenkette im Speicher
Nach dem Aufruf ausleihen(titel) gehört der String „Momo“ dem Parameter buch; titel in main ist ungültig.
  1. Analysiere das Programm Zeile für Zeile: Wer ist nach jeder Anweisung in main Besitzer des Strings „Momo“, und wo wird nur geliehen?
  2. Begründe, warum der Compiler das Programm ablehnt, und warum ein entsprechender Fehler in C gefährlich sein könnte.
  3. Implementiere eine korrigierte Version, bei der alle drei Ausgaben erscheinen. Ändere dazu möglichst wenig.
  4. Sara meint: „Rust ist sicherer als Go, also sollte die Schülerfirma alles in Rust schreiben.“ Bewerte diese Aussage.

Hinweise

Hinweis zu Aufgabe a)
Unterscheide &titel (leihen) und titel (übergeben). Die Abbildung zeigt den Zustand nach der dritten Zeile von main.
Hinweis zu Aufgabe b)
Was passiert mit dem String, wenn ausleihen endet? Denk an „Besitzer wird ungültig“.
Hinweis zu Aufgabe c)
Eine Funktion kann den Wert auch nur leihen.
Hinweis zu Aufgabe d)
Kriterien: Sicherheit, Lernaufwand, Entwicklungszeit, Einsatzgebiet (z. B. Webserver).

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)

let titel = String::from("Momo");: titel ist Besitzer. anzeigen(&titel): anzeigen leiht den String nur, titel bleibt Besitzer. ausleihen(titel): Der Besitz wandert (move) zum Parameter buch; titel ist ab jetzt ungültig. Am Ende von ausleihen wird buch ungültig und der String freigegeben. Das letzte anzeigen(&titel) will einen String leihen, den titel nicht mehr besitzt.

Erwartungshorizont zu Aufgabe b)

Der Borrow Checker prüft beim Kompilieren, dass nur gültige Besitzer benutzt werden. titel ist nach dem Move ungültig, und der Speicher von „Momo“ ist bereits freigegeben — deshalb wird das Programm abgelehnt. In C würde man in so einem Fall über einen Zeiger auf bereits freigegebenen Speicher zugreifen: Das Programm liest dann zufällige Daten, stürzt ab oder wird zur Sicherheitslücke — und der Fehler fällt erst während des Laufs (oder nie) auf.

Erwartungshorizont zu Aufgabe c)

Kleinste Änderung: ausleihen leiht den String auch nur.

Kopf ändern zu fn ausleihen(buch: &String) { und in main aufrufen mit ausleihen(&titel);

Ausgabe: Im Regal: Momo, Ausgeliehen: Momo, Im Regal: Momo.

Erwartungshorizont zu Aufgabe d)

Richtig ist: Rust verhindert Speicherfehler schon beim Kompilieren und braucht keinen Garbage Collector. Aber auch Go ist speichersicher — der Garbage Collector verhindert Zugriffe auf freigegebenen Speicher. Rust ist deutlich schwerer zu lernen, und die Entwicklung dauert länger. Für einen Schul-Webserver mit vielen gleichzeitigen Anfragen ist Go mit Goroutinen einfach und schnell genug. Fazit: Die Aussage ist zu pauschal — Rust lohnt sich, wo höchste Geschwindigkeit und Kontrolle nötig sind; für die Schülerfirma ist Go (oder eine bereits bekannte Sprache) meist die bessere Wahl.