Die Kasse der Klassenfahrt
AFB I–IIDie 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.
package main
import "fmt"
func main() {
beitraege := []int{25, 30, 25, 40}
summe := 0
for _, b := range beitraege {
summe += b
}
fmt.Println("Summe:", summe)
}- Nenne drei Stellen, an denen sich das Go-Programm in der Schreibweise von einem Java-Programm unterscheidet.
- Bestimme die Ausgabe des Programms.
- Pias Freund ergänzt die Zeile
anzahl := len(beitraege), benutztanzahlaber nirgends. Das Programm lässt sich danach nicht mehr übersetzen. Erläutere, warum Go hier so streng ist.
Hinweise
Hinweis zu Aufgabe a)
Hinweis zu Aufgabe b)
Hinweis zu Aufgabe c)
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.
Die Bibliotheks-App
AFB II–IIIDie 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`.
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);
}- Analysiere das Programm Zeile für Zeile: Wer ist nach jeder Anweisung in
mainBesitzer des Strings „Momo“, und wo wird nur geliehen? - Begründe, warum der Compiler das Programm ablehnt, und warum ein entsprechender Fehler in C gefährlich sein könnte.
- Implementiere eine korrigierte Version, bei der alle drei Ausgaben erscheinen. Ändere dazu möglichst wenig.
- Sara meint: „Rust ist sicherer als Go, also sollte die Schülerfirma alles in Rust schreiben.“ Bewerte diese Aussage.
Hinweise
Hinweis zu Aufgabe a)
&titel (leihen) und titel (übergeben). Die Abbildung zeigt den Zustand nach der dritten Zeile von main.Hinweis zu Aufgabe b)
ausleihen endet? Denk an „Besitzer wird ungültig“.Hinweis zu Aufgabe c)
Hinweis zu Aufgabe d)
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.
