# Papierkorb unter Rust: Crate, Netzlaufwerke, Kosten **Gitea-Issue:** #21 · **Stand der Recherche:** 2026-08-28 **Umfeld:** DioNotes, Rust (edition 2021), Dioxus 0.7.1, Default-Feature `desktop`. Entwicklung und Messung unter Windows 11 Enterprise 10.0.26200, cargo/rustc 1.96.0. Alle Aussagen sind als **[belegt]** (Primärquelle mit URL, Quellcode, oder eigene reproduzierbare Messung) oder **[Vermutung]** (Schlussfolgerung ohne deckende Quelle) markiert. > **Besonderheit dieser Recherche:** Die Kernfrage — was auf Netzlaufwerken passiert — wurde > **nicht erschlossen, sondern gemessen**. Auf dem Entwicklungsrechner sind SMB-Netzlaufwerke > gemountet (`Z:` → `\\la-nas-01.ad.datawin.de\Scan`). Es wurden drei kleine Wegwerf-Programme > außerhalb des Repos gebaut und ausgeführt. Die Rohausgaben stehen in Abschnitt 4. > Im Repo wurde **keine Dependency hinzugefügt**; `Cargo.toml` und `Cargo.lock` sind unverändert. --- ## 1. Kurzantwort **Die Crate ist `trash`, aktuell `5.2.6`** (veröffentlicht 2026-05-03). Sie ist der De-facto-Standard und wird gepflegt. **Aufruf:** ```rust trash::delete(&pfad)?; // eine Datei trash::delete_all(&pfade)?; // mehrere ``` **Kosten:** **+6 Pakete** im Windows-Graphen. Das ist **nicht** über Cargo-Features reduzierbar — das im Ticket vermutete Gegenstück zum `rfd`/Wayland-Fall **gibt es hier nicht**. Fünf der sechs Pakete sind eine **zweite, ältere Kopie der `windows`-Crate-Familie** (0.56 neben dem bereits vorhandenen 0.61.3), weil `trash 5.2.6` noch auf `windows 0.56.0` festgenagelt ist. **Netzlaufwerke — und hier wird es unangenehm, es sind zwei getrennte Probleme:** 1. **`trash 5.2.6` funktioniert auf Netzlaufwerken schlicht gar nicht.** Der Aufruf schlägt mit einer irreführenden Meldung fehl (`0x80070002`, „Das System kann die angegebene Datei nicht finden"), **die Datei bleibt unangetastet liegen**. Ursache ist ein Pfad-Bug in der Crate, kein Papierkorb-Thema. Immerhin: **nichts geht verloren.** 2. **Wäre der Bug behoben, wäre das Verhalten gefährlich.** Ruft man dieselbe Windows-API mit denselben Flags, aber unverbogenem Pfad auf, dann wird die Datei auf dem Netzlaufwerk **endgültig und stillschweigend gelöscht** — `PerformOperations()` liefert `Ok(())`, `GetAnyOperationsAborted()` liefert `false`. Am Rückgabewert ist der Fall **nicht** vom erfolgreichen Papierkorb-Verschieben zu unterscheiden. **Die Antwort auf die eigentliche Ticketfrage lautet also: Ja, die App braucht eine Vorab-Prüfung bzw. Rückfrage.** Sich auf `Ok(())` zu verlassen, reicht nicht. **Erkennen lässt sich der Fall zuverlässig** — aber nur über `IFileOperationProgressSink`, den `trash` nicht durchreicht (die Crate übergibt hart `None`). Details in Abschnitt 5. **Blockierend.** Die Dauer ist unabhängig von der Dateigröße (Verschieben innerhalb des Volumes); gemessen: ~740 ms beim allerersten Aufruf (COM-Initialisierung), danach ~27 ms. Abschnitt 6. --- ## 2. Welche Crate, wird sie gepflegt? ### [belegt] - **`trash`, aktuelle Version `5.2.6`**, veröffentlicht **2026-05-03**, nicht zurückgezogen. 2.683.466 Downloads gesamt, 724.229 „recent". Beschreibung: „A library for moving files and folders to the Recycle Bin". → - Letzte Releases: 5.2.6 (2026-05-03), 5.2.5 (2025-10-25), 5.2.4 (2025-10-24), 5.2.3 (2025-08-15), 5.2.2 (2025-02-22). Also **regelmäßige Releases, zuletzt vor knapp vier Monaten**. → - Das in `Cargo.toml` eingetragene Repository ist `https://github.com/ArturKovacs/trash`; gepflegt und released wird faktisch aus dem Fork **** (251 Sterne), auf den auch die CI-Badges und alle Changelog-Commit-Links zeigen. → `trash-5.2.6/README.md`, `trash-5.2.6/CHANGELOG.md` - Unterstützte Plattformen laut README: „Windows, macOS, and all FreeDesktop Trash compliant environments (including GNOME, KDE, XFCE, and more)". → `trash-5.2.6/README.md` - `rust-version = "1.85.0"`, `edition = "2021"`, Lizenz MIT. → `trash-5.2.6/Cargo.toml` - Das Changelog von 5.2.6 zeigt echte, inhaltliche Wartung (Panic durch sauberen Fehler ersetzt, weil `cosmic-files` abstürzte) — die Crate hat reale, anspruchsvolle Nutzer. → `trash-5.2.6/CHANGELOG.md` ### [Vermutung] Es gibt keine ernstzunehmende Alternative. `rrecycle` und `recyclebin` tauchen in der Suche auf, sind aber ungleich kleiner und teils reine CLI-Werkzeuge. Für DioNotes ist `trash` gesetzt. --- ## 3. Was kostet das im Abhängigkeitsgraphen? Das Ticket fragt gezielt nach einem Gegenstück zum `rfd`-Fall, wo sich neun Wayland-Pakete durch Angleichen der Features an `dioxus-desktop` vollständig vermeiden ließen. ### [belegt] Antwort: Ein solches Gegenstück gibt es nicht. Die Features von `trash 5.2.6` lauten wörtlich: ```toml [features] coinit_apartmentthreaded = [] coinit_disable_ole1dde = [] coinit_multithreaded = [] coinit_speed_over_memory = [] default = ["coinit_apartmentthreaded", "chrono"] ``` → `trash-5.2.6/Cargo.toml` **Alle vier `coinit_*`-Features haben leere Dependency-Listen** — es sind reine `cfg`-Schalter für den COM-Initialisierungsmodus (`COINIT_APARTMENTTHREADED` vs. `COINIT_MULTITHREADED`), ohne jede Wirkung auf den Graphen. → `trash-5.2.6/src/windows.rs`, `struct CoInitializer` Das einzige dependency-wirksame Feature ist `chrono`, und das ist **ausschließlich unter `cfg(all(unix, not(macos), not(ios), not(android)))`** deklariert. **Auf Windows ist es wirkungslos.** → `trash-5.2.6/Cargo.toml`, Abschnitt `[target.'cfg(all(unix, …))'.dependencies.chrono]` ### [belegt] Gemessene Zahlen Basis: `Cargo.lock` im Repo mit 745 Paketen; `cargo tree --edges normal` ergibt 419 eindeutige Pakete für `x86_64-pc-windows-msvc` und 470 für `x86_64-unknown-linux-gnu`. | Variante | Windows-Target | Linux-Target | |---|---|---| | `cargo add trash` (default) | **+6** (419 → 425) | +4 (470 → 474) | | `cargo add trash --no-default-features` | **+6** (keine Ersparnis) | +2 (470 → 472) | Die sechs neuen Pakete auf Windows: ``` trash v5.2.6 windows v0.56.0 windows-core v0.56.0 windows-implement v0.56.0 (proc-macro) windows-interface v0.56.0 (proc-macro) windows-result v0.1.2 ``` ### [belegt] Warum die `windows`-Pakete nicht „gratis" sind Sie stehen zwar bereits namentlich im `Cargo.lock`, aber in **semver-inkompatiblen** Versionen, werden also **nicht** dedupliziert: | Crate | schon im Lock (via dioxus/wry) | von `trash` gefordert | dedupliziert? | |---|---|---|---| | `windows` | 0.61.3 | **0.56.0** | nein | | `windows-core` | 0.61.2 | **0.56.0** | nein | | `windows-implement` | 0.60.2 | **0.56.0** | nein | | `windows-interface` | 0.59.3 | **0.56.0** | nein | | `windows-result` | 0.3.4 | **0.1.2** | nein | `cargo tree -i windows@0.56.0` bestätigt: `trash` ist der einzige Konsument dieser zweiten Kopie. Wirklich gratis sind nur `log` (0.4.33) und `scopeguard` (1.2.0), die bereits passend im Graphen stehen. ### [belegt] Auf Linux kostet es fast nichts Die FreeDesktop-Implementierung ist reines Rust (`libc`, `once_cell`, `scopeguard`, `urlencoding`, optional `chrono`). **Kein GTK, kein dbus, kein zbus** — der `rfd`-artige Fallstrick existiert hier nicht. → `trash-5.2.6/Cargo.toml` Mit `--no-default-features` fielen auf Linux `chrono` + `iana-time-zone` weg (+2 statt +4). Preis: `trash` kann dann keinen `DeletionDate`-Zeitstempel in die `.trashinfo`-Dateien schreiben. Das ist funktional relevant, nicht bloß kosmetisch. **[Vermutung]** Für DioNotes wäre der Verzicht auf zwei Pakete den Verlust der Zeitstempel nicht wert. ### [Vermutung] Der einzige echte Hebel Ein künftiges `trash`-Release, das auf `windows` ≥ 0.61 aktualisiert, würde die gesamte `windows`-Familie mit dem bestehenden dioxus/wry-Graphen deduplizieren. Der Windows-Delta fiele dann von 6 auf **1** (nur `trash` selbst). Das ist nichts, worauf man warten sollte, aber es ist der Grund, die Version im Blick zu behalten. --- ## 4. Netzlaufwerke und USB-Sticks — die Kernfrage Das Ticket fragt: Fällt der Aufruf still auf endgültiges Löschen zurück, oder schlägt er fehl? **Die Antwort hat zwei Schichten, und sie sind unabhängig voneinander.** ### 4.1 [belegt] Schicht 1: `trash 5.2.6` schlägt auf Netzlaufwerken fehl — wegen eines Pfad-Bugs Gemessen, Wegwerf-Programm gegen `trash 5.2.6`, Datei auf `Z:\` (SMB-Share): ``` === C: Netzlaufwerk SMB, klein | dir=Z:\ | 1024 bytes === angelegt: Z:\dionotes-trashprobe-net-small.md trash::delete -> Err(Os { code: -2147024894, description: "windows error: Das System kann die angegebene Datei nicht finden. (0x80070002)" }) Dauer: 31.0407ms Datei existiert noch? true Papierkorb-Treffer: vorher=0 nachher=0 => IM PAPIERKORB: false ``` Zum Vergleich, dieselbe Operation lokal auf NTFS: ``` === A: lokal NTFS, klein | dir=C:\…\Temp | 1024 bytes === trash::delete -> Ok(()) Dauer: 740.7595ms Datei existiert noch? false Papierkorb-Treffer: vorher=0 nachher=1 => IM PAPIERKORB: true ``` **Die Datei auf dem Netzlaufwerk bleibt liegen. Es geht nichts verloren, aber es passiert auch nichts.** Das reproduziert exakt den seit 2022-08-07 offenen Bug [trash-rs#55 „Unable to delete file on Windows shared folder"](https://github.com/Byron/trash-rs/issues/55) (Label `help wanted`, bis heute ungelöst). **Die Ursache, aus dem Quellcode und nachgemessen:** `trash` kanonisiert jeden Pfad vor dem Löschen mit `std::fs::canonicalize`: ```rust let canonical_parent = parent.canonicalize() .map_err(|_| Error::CanonicalizePath { original: parent.to_owned() })?; ``` → `trash-5.2.6/src/lib.rs`, `fn canonicalize_paths` `windows.rs` schneidet danach stumpf die ersten vier UTF-16-Zeichen `\\?\` ab: ```rust let path_prefix = ['\\' as u16, '\\' as u16, '?' as u16, '\\' as u16]; let wide_path_container = to_wide_path(full_path); let wide_path_slice = if wide_path_container.starts_with(&path_prefix) { &wide_path_container[path_prefix.len()..] } else { &wide_path_container[0..] }; ``` → `trash-5.2.6/src/windows.rs` Das geht für lokale Pfade gut und für UNC-Pfade schief. Gemessen: ``` ### canonicalize-Verhalten fuer lokal C: canonicalize() = "\\?\C:\Users\marvin.dietermann\AppData\Local\Temp" nach dem Prefix-Stripping = "C:\Users\marvin.dietermann\AppData\Local\Temp" <- gültig ### canonicalize-Verhalten fuer Netzlaufwerk Z: canonicalize() = "\\?\UNC\la-nas-01.ad.datawin.de\Scan" nach dem Prefix-Stripping = "UNC\la-nas-01.ad.datawin.de\Scan" <- Unsinn ### canonicalize-Verhalten fuer UNC-Pfad \\la-nas-01.ad.datawin.de\Scan canonicalize() = "\\?\UNC\la-nas-01.ad.datawin.de\Scan" nach dem Prefix-Stripping = "UNC\la-nas-01.ad.datawin.de\Scan" <- Unsinn ``` Aus `\\?\UNC\server\share` wird `UNC\server\share` — ein Pfad, den es nicht gibt. `SHCreateItemFromParsingName` scheitert daran mit `0x80070002`. Korrekt wäre die Rückübersetzung nach `\\server\share`. **Betroffen sind beide Schreibweisen** — der gemappte Laufwerksbuchstabe `Z:\` genauso wie der direkte UNC-Pfad, weil `canonicalize` beide auf dieselbe `\\?\UNC\…`-Form abbildet. ### 4.2 [belegt] Schicht 2: Windows selbst löscht auf Netzlaufwerken still und endgültig Um Crate-Bug von Betriebssystemverhalten zu trennen, wurde `IFileOperation` **direkt** aufgerufen — mit exakt denselben Flags wie `trash` (`FOF_NO_UI | FOF_ALLOWUNDO | FOF_WANTNUKEWARNING`, zusammen `0x00004654`), aber mit unverbogenem Pfad: ``` ### IFileOperation direkt auf lokal C: PerformOperations -> Ok(()) (Dauer 102.5266ms) GetAnyOperationsAborted -> false Datei existiert noch? false Papierkorb-Treffer: 1 <- im Papierkorb ### IFileOperation direkt auf Netzlaufwerk Z: PerformOperations -> Ok(()) (Dauer 405.4515ms) GetAnyOperationsAborted -> false Datei existiert noch? false Papierkorb-Treffer: 0 <- WEG. endgueltig. ### IFileOperation direkt auf UNC-Pfad \\la-nas-01.ad.datawin.de\Scan PerformOperations -> Ok(()) (Dauer 299.5401ms) GetAnyOperationsAborted -> false Datei existiert noch? false Papierkorb-Treffer: 0 <- WEG. endgueltig. ``` **Das ist die eigentliche Warnung des Tickets.** Auf dem Netzlaufwerk wird die Datei endgültig gelöscht, und die API meldet **denselben Erfolg** wie beim Verschieben in den Papierkorb: `Ok(())` und `GetAnyOperationsAborted() == false`. **Am Rückgabewert ist der Unterschied nicht erkennbar.** Bemerkenswert: **`FOF_WANTNUKEWARNING` hat keinen Dialog ausgelöst.** Microsoft dokumentiert das Flag als „Send a warning if a file or folder is being destroyed during a delete operation rather than recycled. This flag partially overrides **FOF_NOCONFIRMATION**." → **[Vermutung]** Die Warnung unterbleibt, weil `trash` zusätzlich `FOF_SILENT` und `FOF_NOERRORUI` setzt: `FOF_NO_UI` ist in der `windows`-Crate als `1556u32` = `0x614` definiert, also `FOF_SILENT (0x4) | FOF_NOCONFIRMATION (0x10) | FOF_NOCONFIRMMKDIR (0x200) | FOF_NOERRORUI (0x400)`. → `windows-0.56.0/src/Windows/Win32/UI/Shell/mod.rs`, `pub const FOF_NO_UI` Das „partially overrides" bezieht sich laut Doku nur auf `FOF_NOCONFIRMATION`, nicht auf `FOF_SILENT`/`FOF_NOERRORUI`. Belegt ist jedenfalls das Ergebnis: **kein Dialog, keine Rückfrage, Datei weg.** ### 4.3 USB-Sticks und andere Dateisysteme — [nicht belastbar beantwortet] **Das konnte nicht gemessen werden.** Auf dem Testrechner sind zwar zwei Wechseldatenträger (`D:`, `E:`) angemeldet, beide sind aber leer (`FileSystemType = Unknown`, `SizeRemaining = 0`) — offensichtlich Kartenleser ohne Medium. Ohne echten FAT32-/exFAT-Stick lässt sich der Fall nicht verifizieren. **[Vermutung]**, ausdrücklich als solche gekennzeichnet: Lokale Wechseldatenträger verhalten sich anders als Netzlaufwerke; Windows legt auf ihnen üblicherweise sehr wohl einen `$RECYCLE.BIN`- bzw. `RECYCLER`-Ordner an. Der kritische Fall ist damit **[Vermutung]** primär das Netzlaufwerk. Verlassen sollte man sich darauf nicht — die in Abschnitt 5 beschriebene Erkennung deckt beide Fälle ab, ohne dass man vorher wissen muss, welcher vorliegt. **Vor der Implementierung sollte dieser Fall mit einem echten USB-Stick nachgemessen werden.** Ebenfalls **[Vermutung]**: Auch auf lokalem NTFS kann der Papierkorb fehlen — er lässt sich pro Volume in den Papierkorb-Eigenschaften deaktivieren („Dateien sofort löschen"), und Dateien oberhalb der konfigurierten Maximalgröße gehen ebenfalls direkt verloren. Eine Prüfung, die nur auf den Laufwerkstyp schaut, greift deshalb zu kurz. --- ## 5. Wie erfährt das Programm, welcher Fall eingetreten ist? ### [belegt] Über den Rückgabewert von `trash`: gar nicht `trash::delete` liefert `Result<(), Error>`. Die `Error`-Varianten sind `Unknown`, `Os`, `FileSystem` (nur FreeDesktop), `TargetedRoot`, `CouldNotAccess`, `CanonicalizePath`, `ConvertOsString`, `RestoreCollision`, `RestoreTwins`. → **Es gibt keine Variante und kein Erfolgs-Detail, das „endgültig gelöscht statt Papierkorb" ausdrückt.** Wie Abschnitt 4.2 zeigt, kommt in diesem Fall schlicht `Ok(())` zurück. Die Crate wertet lediglich `GetAnyOperationsAborted()` aus, und das war im Netzlaufwerk-Fall `false`: ```rust pfo.PerformOperations()?; if pfo.GetAnyOperationsAborted()?.as_bool() { // TODO: return the reason why the operation was aborted. return Err(Error::Unknown { description: "Some operations were aborted".into() }); } ``` → `trash-5.2.6/src/windows.rs` ### [belegt] Über `IFileOperationProgressSink`: zuverlässig — aber `trash` reicht ihn nicht durch Microsoft dokumentiert für `IFileOperationProgressSink::PostDeleteItem` den Parameter `psiNewlyCreated` wörtlich so: > „A pointer to an **IShellItem** that specifies the deleted item, now in the Recycle Bin. > **If the item was fully deleted, this value is NULL.**" → Genau das wurde nachgemessen — ein eigener Sink, angehängt an `DeleteItem`: ``` ### lokal C: [sink] PreDeleteItem dwFlags=0x00000282 [sink] PostDeleteItem dwFlags=0x00000282 hrDelete=0x00270008 psiNewlyCreated=Some ==> IM PAPIERKORB: true neuerPfad=Some("C:\$Recycle.Bin\S-1-5-21-…-1668\$R1V9IFD.md") ### Netzlaufwerk Z: [sink] PreDeleteItem dwFlags=0x00000202 [sink] PostDeleteItem dwFlags=0x00000202 hrDelete=0x00270008 psiNewlyCreated=NULL ==> IM PAPIERKORB: false neuerPfad=None ``` **Zwei verwertbare Signale, beide belegt:** 1. **`psiNewlyCreated` in `PostDeleteItem`** — `Some` ⇒ im Papierkorb (inklusive konkretem Pfad im `$Recycle.Bin`), `NULL` ⇒ endgültig gelöscht. Das ist die verlässliche Nachher-Auskunft. `hrDelete` taugt **nicht** dafür: es war in beiden Fällen identisch `0x00270008`. 2. **`dwFlags` schon in `PreDeleteItem`, also *vor* dem Löschen.** Die Differenz zwischen `0x282` und `0x202` ist genau `0x80` — und das ist `TSF_DELETE_RECYCLE_IF_POSSIBLE`: ``` pub const TSF_DELETE_RECYCLE_IF_POSSIBLE: _TRANSFER_SOURCE_FLAGS = _TRANSFER_SOURCE_FLAGS(128i32); pub const TSF_COPY_LOCALIZED_NAME: _TRANSFER_SOURCE_FLAGS = _TRANSFER_SOURCE_FLAGS(512i32); pub const TSF_OVERWRITE_EXIST: _TRANSFER_SOURCE_FLAGS = _TRANSFER_SOURCE_FLAGS(2i32); ``` → `windows-0.56.0/src/Windows/Win32/UI/Shell/mod.rs` Auf `C:` ist das Bit gesetzt, auf `Z:` fehlt es. **Windows sagt also im `PreDeleteItem`-Callback von sich aus, ob es recyceln wird** — bevor irgendetwas passiert ist. Und man kann an dieser Stelle abbrechen: Microsoft dokumentiert für die Sink-Rückgabewerte „Returns S_OK if successful, or an error value otherwise. In the case of an error value, all subsequent operations pending from the call to IFileOperation are canceled." → **[Vermutung]** Damit ließe sich aus `PreDeleteItem` heraus vetoen und stattdessen erst der Nutzer fragen. Der Abbruch aus `PreDeleteItem` wurde **nicht** gemessen. **Der Haken:** `trash` übergibt den Sink-Parameter hart als `None`: ```rust pfo.DeleteItem(&shi, None)?; ``` → `trash-5.2.6/src/windows.rs` Über die Crate-API ist an diese Information **nicht** heranzukommen. ### [Vermutung] Konsequenz für DioNotes Drei gangbare Wege, in aufsteigender Sauberkeit: 1. **Billig und grob:** vor dem Löschen prüfen, ob der Workspace-Pfad auf einem Netzlaufwerk liegt (Laufwerkstyp `DRIVE_REMOTE` bzw. UNC-Pfad), und dann eine Rückfrage stellen. Erkennt den Hauptfall, aber nicht den deaktivierten Papierkorb auf lokalem NTFS. 2. **Nachher-Prüfung:** nach `trash::delete` per `trash::os_limited::list()` nachsehen, ob der Eintrag im Papierkorb aufgetaucht ist. Funktioniert (so wurden die Messungen oben verifiziert), ist aber langsam und rennt gegen Race Conditions. 3. **Sauber:** `IFileOperation` mit eigenem Sink selbst aufrufen, statt `trash` zu benutzen — dann bekommt man `TSF_DELETE_RECYCLE_IF_POSSIBLE` vorab und `psiNewlyCreated` hinterher, und der UNC-Bug aus 4.1 entfällt ebenfalls, weil man `canonicalize` nicht braucht. Kostet dafür eine eigene Windows-Implementierung und hilft auf Linux/macOS nicht. Da `trash 5.2.6` auf Netzlaufwerken ohnehin nicht funktioniert, ist die Entscheidung ohne eine bewusste Behandlung dieses Falls **nicht** vollständig — deshalb ist dieses Ticket die Voraussetzung für die Lösch-Implementierung, nicht bloß eine Fußnote. --- ## 6. Blockierend oder asynchron? Wie lange dauert es? ### [belegt] Blockierend `trash::delete` ist eine gewöhnliche synchrone Funktion; es gibt keine `async`-Variante in der öffentlichen API. Der Windows-Pfad ruft `pfo.PerformOperations()` direkt im aufrufenden Thread auf, ohne Thread zu spawnen. → `trash-5.2.6/src/windows.rs`, ### [belegt] Gemessene Dauer | Fall | Größe | Dauer | |---|---|---| | lokal NTFS, erster Aufruf im Prozess | 1 KiB | **740,8 ms** | | lokal NTFS, zweiter Aufruf | **512 MiB** | **26,7 ms** | | `IFileOperation` direkt, lokal | 5 B | 102,5 ms | | `IFileOperation` direkt, Netzlaufwerk `Z:` | 5 B | 405,5 ms | | `IFileOperation` direkt, UNC-Pfad | 5 B | 299,5 ms | **Die Dateigröße spielt praktisch keine Rolle:** 512 MiB wurden in 26,7 ms erledigt — schneller als die 1-KiB-Datei davor. Das Verschieben in den Papierkorb ist ein Rename **innerhalb desselben Volumes**, es werden keine Daten kopiert. Was kostet, ist der **erste** Aufruf im Prozess: die COM-Initialisierung (`CoInitializeEx` + `CoCreateInstance` der `FileOperation`-Klasse) schlägt mit gut 0,7 s zu Buche. `trash` initialisiert COM `thread_local` und einmalig. → `trash-5.2.6/src/windows.rs`, `thread_local! { static CO_INITIALIZER … }` **[Vermutung]** Für DioNotes heißt das: der Aufruf gehört nicht in den UI-Thread. Ein `spawn_blocking` bzw. der `tokio`-Threadpool genügt völlig; eine Fortschrittsanzeige braucht es für einzelne `.md`-Dateien nicht. Netzlaufwerke sind mit ~0,3–0,4 s deutlich träger als lokale Platten, aber immer noch weit unter jeder Schmerzgrenze. **Achtung [belegt]:** `trash` initialisiert COM standardmäßig mit `COINIT_APARTMENTTHREADED` (Feature `coinit_apartmentthreaded`, Teil der Defaults) und **panickt**, wenn `CoInitializeEx` fehlschlägt: ```rust panic!("Call to CoInitializeEx failed. HRESULT: {:?}. Consider using `trash` with the feature `coinit_multithreaded`", hr); ``` → `trash-5.2.6/src/windows.rs` **[Vermutung]** Ruft man `trash` aus einem Tokio-Worker-Thread auf, der bereits als MTA initialisiert wurde, könnte das zuschlagen; dann ist `default-features = false` plus `features = ["coinit_multithreaded"]` der Ausweg. Das wurde **nicht** gemessen. --- ## 7. macOS und Linux ### [belegt] macOS `trash` bietet zwei Verfahren, wählbar über `TrashContext`: - **`DeleteMethod::Finder` (Default)** — steuert den Finder per AppleScript. - **`DeleteMethod::NsFileManager`** — ruft `trashItemAtURL` auf `NSFileManager` auf. Laut den Doc-Kommentaren: „Somewhat faster than the `Finder` method", „Does *not* require additional permissions", „Does *not* produce the sound that Finder usually makes", aber „Does *not* show the 'Put Back' option on some systems … This is a macOS bug." → `trash-5.2.6/src/macos/mod.rs` Fehler von `trashItemAtURL` werden als `Error::Unknown` weitergereicht, nicht verschluckt. Abhängigkeiten: `objc2`, `objc2-foundation`, `percent-encoding` — alle drei stehen **bereits im `Cargo.lock`** (0.6.4 / 0.3.2 / 2.3.2), auf macOS wäre `trash` also nahezu gratis. ### [belegt] Linux / FreeDesktop Implementiert die FreeDesktop-Trash-Spezifikation v1.0. Die Implementierung ist reines Rust, ohne GTK/dbus. Wichtig für die Netzlaufwerk-Frage: Der Code **legt die Trash-Ordner bei Bedarf an** und fällt bei einem geräteübergreifenden Verschieben explizit auf Kopieren-und-Löschen zurück: ```rust let Err(e) = std::fs::rename(src, dst) else { return Ok(()) }; let needs_cross_device_copy = e.kind() == ErrorKind::CrossesDevices; if !needs_cross_device_copy { return Err((src.to_owned(), e)); } debug!("Cross-device move detected, falling back to copy+delete for {:?}", src); ``` → `trash-5.2.6/src/freedesktop.rs` **[Vermutung]** Damit ist Linux der unproblematischere Fall: Entweder die Datei landet in einem Trash-Verzeichnis, oder es kommt ein `Error::FileSystem` zurück. Ein stilles endgültiges Löschen wie unter Windows ist im Code nicht angelegt. Nicht gemessen — es stand keine Linux-Maschine zur Verfügung. --- ## 8. Empfehlung **[Vermutung]** — das ist eine Einschätzung, keine belegte Aussage: 1. **`trash 5.2.6` nehmen**, mit Default-Features. Die +6 Pakete sind der Preis; verhandeln lässt sich daran nichts. 2. **Den Netzlaufwerk-Fall vor der ersten Löschfunktion klären, nicht danach.** `trash` schlägt dort heute fehl (4.1) — die App muss diesen Fehler ohnehin abfangen und dem Nutzer erklären, sonst wirkt das Löschen auf Netzlaufwerken schlicht kaputt. 3. **Eine Rückfrage vor dem Löschen ist nötig**, sobald der Workspace nicht auf einer lokalen Platte liegt. Der Nutzer muss wissen, ob seine Notiz wiederherstellbar ist — und aus dem Rückgabewert von `trash` erfährt er es nicht (5). 4. **Nicht im UI-Thread aufrufen** (6). 5. **Die `trash`-Version im Blick behalten**: sobald sie auf `windows` ≥ 0.61 geht, halbieren sich die Kosten, und `trash-rs#55` könnte bei der Gelegenheit mit erledigt werden. --- ## 9. Reproduktion Die Messungen stammen aus drei Wegwerf-Programmen, die **außerhalb des Repos** gebaut wurden (Scratchpad, nicht eingecheckt): - `main.rs` — `trash::delete` gegen lokal / Netzlaufwerk, mit Papierkorb-Gegenprobe über `trash::os_limited::list()` - `raw.rs` — `IFileOperation` direkt, mit denselben Flags, ohne `canonicalize`; zeigt zusätzlich das Prefix-Stripping-Verhalten - `sink.rs` — eigener `IFileOperationProgressSink` (`#[implement]` aus `windows 0.56`), protokolliert `PreDeleteItem`/`PostDeleteItem` Alle angelegten Testdateien wurden wieder entfernt, die Papierkorb-Einträge mit `trash::os_limited::purge_all` endgültig gelöscht. Auf `Z:` sind keine Rückstände verblieben. **Im Repo wurde nichts an `Cargo.toml` oder `Cargo.lock` geändert** — die Dependency-Messung lief über `cargo add` mit anschließendem `git checkout --`, verifiziert mit leerem `git status --porcelain`. --- ## 10. Quellen - crates.io-API zu `trash`: - API-Doku: - Quelltext `lib.rs` (Error-Enum, `canonicalize_paths`): - Quelltext `windows.rs`, `freedesktop.rs`, `macos/mod.rs`, `Cargo.toml`, `README.md`, `CHANGELOG.md`: aus dem Crate-Tarball (auf docs.rs **nicht** einsehbar — docs.rs baut auf Linux, `windows.rs` ist dort wegcfg-t) - Repository (gepflegter Fork): - Offener Bug zum Netzlaufwerk-Fall: - `IFileOperation::SetOperationFlags` (FOF_-/FOFX_-Flags, `FOF_WANTNUKEWARNING`): - `IFileOperationProgressSink::PostDeleteItem` (`psiNewlyCreated` ist NULL bei endgültiger Löschung): - Konstanten `FOF_NO_UI`, `TSF_DELETE_RECYCLE_IF_POSSIBLE` u. a.: generierte Bindings in `windows 0.56.0`, `src/Windows/Win32/UI/Shell/mod.rs` - FreeDesktop Trash Specification v1.0: