{"id":13637124,"url":"https://github.com/myrrlyn/tap","last_synced_at":"2025-12-30T02:45:40.185Z","repository":{"id":43046279,"uuid":"295171215","full_name":"myrrlyn/tap","owner":"myrrlyn","description":"Generic extensions for tapping values in Rust.","archived":false,"fork":true,"pushed_at":"2023-09-29T17:27:36.000Z","size":30,"stargazers_count":411,"open_issues_count":12,"forks_count":15,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-11-08T19:46:17.533Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"darfink/tap-rs","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/myrrlyn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-13T14:42:23.000Z","updated_at":"2024-11-05T20:40:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/myrrlyn/tap","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/myrrlyn%2Ftap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/myrrlyn%2Ftap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/myrrlyn%2Ftap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/myrrlyn%2Ftap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/myrrlyn","download_url":"https://codeload.github.com/myrrlyn/tap/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223795263,"owners_count":17204136,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T00:01:10.936Z","updated_at":"2025-12-12T16:55:34.156Z","avatar_url":"https://github.com/myrrlyn.png","language":"Rust","readme":"\u003cdiv class=\"title-block\" style=\"text-align: center;\" align=\"center\"\u003e\n\n# `tap` \u003c!-- omit in toc --\u003e\n\n## Suffix-Position Pipeline Behavior \u003c!-- omit in toc --\u003e\n\n[![Crate][crate_img]][crate]\n[![Documentation][docs_img]][docs]\n[![License][license_img]][license_file]\n\n[![Crate Downloads][downloads_img]][crate]\n[![Crate Size][loc_img]][loc]\n\n\u003c/div\u003e\n\nThis crate provides extension methods on all types that allow transparent,\ntemporary, inspection/mutation (tapping), transformation (piping), or type\nconversion. These methods make it convenient for you to insert debugging or\nmodification points into an expression without requiring you to change any other\nportions of your code.\n\n## Example Use\n\n### Tapping\n\nYou can tap inside a method-chain expression for logging without requiring a\nrebind. For instance, you may write a complex expression without any\nintermediate debugging steps, and only later decide that you want them.\nOrdinarily, this transform would look like this:\n\n```rust\nextern crate reqwest;\nextern crate tracing;\n\n// old\nlet body = reqwest::blocking::get(\"https://example.com\")?\n  .text()?;\ntracing::debug!(\"Response contents: {}\", body);\n\n// new, with debugging\nlet resp = reqwest::blocking::get(\"https://example.com\")?;\ntracing::debug!(\"Response status: {}\", resp.status());\nlet body = resp.text()?;\ntracing::debug!(\"Response contents: {}\", body);\n```\n\nwhile with tapping, you can plug the logging statement directly into the overall\nexpression, without making any other changes:\n\n```rust\nextern crate reqwest;\nextern crate tracing;\n\nlet body = reqwest::blocking::get(\"https://example.com\")?\n  // The only change is the insertion of this line\n  .tap(|resp| tracing::debug!(\"Response status: {}\", resp.status()))\n  .text()?;\ntracing::debug!(\"Response contents: {}\", body);\n```\n\n### Mutable Tapping\n\nSome APIs are written to require mutable borrows, rather than value-to-value\ntransformations, which can require temporary rebinding in order to create\nmutability in an otherwise-immutable context. For example, collecting data into\na vector, sorting the vector, and then freezing it, might look like this:\n\n```rust\nlet mut collection = stream().collect::\u003cVec\u003c_\u003e\u003e();\ncollection.sort();\n// potential error site: inserting other mutations here\nlet collection = collection; // now immutable\n```\n\nBut with a mutable tap, you can avoid the duplicate binding *and* guard against\nfuture errors due to the presence of a mutable binding:\n\n```rust\nlet collection = stream.collect::\u003cVec\u003c_\u003e\u003e()\n  .tap_mut(|v| v.sort());\n```\n\nThe `.tap_mut()` and related methods provide a mutable borrow to their argument,\nand allow the final binding site to choose their own level of mutability without\nexposing the intermediate permission.\n\n### Piping\n\nIn addition to transparent inspection or modification points, you may also wish\nto use suffix calls for subsequent operations. For example, the standard library\noffers the free function `fs::read` to convert `Path`-like objects into\n`Vec\u003cu8\u003e` of their filesystem contents. Ordinarily, free functions require use\nas:\n\n```rust\nuse std::fs;\n\nlet mut path = get_base_path();\npath.push(\"logs\");\npath.push(\u0026format!(\"{}.log\", today()));\nlet contents = fs::read(path)?;\n```\n\nwhereäs use of tapping (for path modification) and piping (for `fs::read`) could\nbe expressed like this:\n\n```rust\nuse std::fs;\n\nlet contents = get_base_path()\n  .tap_mut(|p| p.push(\"logs\"))\n  .tap_mut(|p| p.push(\u0026format!(\"{}.log\", today())))\n  .pipe(fs::read)?;\n```\n\nAs a clearer example, consider the syntax required to apply multiple free\nfuntions without `let`-bindings looks like this:\n\n```rust\nlet val = last(\n  third(\n    second(\n      first(original_value),\n      another_arg,\n    )\n  ),\n  another_arg,\n);\n```\n\nwhich requires reading the expression in alternating, inside-out, order, to\nunderstand the full sequence of evaluation. With suffix calls, even free\nfunctions can be written in a point-free style that maintains a clear temporal\nand syntactic order:\n\n```rust\nlet val = original_value\n  .pipe(first)\n  .pipe(|v| second(v, another_arg))\n  .pipe(third)\n  .pipe(|v| last(v, another_arg));\n```\n\nAs piping is an ordinary method, not a syntax transformation, it still requires\nthat you write function-call expressions when using a function with multiple\narguments in the pipeline.\n\n### Conversion\n\nThe `conv` module is the simplest: it provides two traits, `Conv` and `TryConv`,\nwhich are sibling traits to `Into\u003cT\u003e` and `TryInto\u003cT\u003e`. Their methods,\n`Conv::conv::\u003cT\u003e` and `TryConv::try_conv::\u003cT\u003e`, call the corresponding\ntrait implementation, and allow you to use `.into()`/`.try_into()` in\nnon-terminal method calls of an expression.\n\n```rust\nlet bytes = \"hello\".into().into_bytes();\n```\n\ndoes not compile, because Rust cannot decide the type of `\"hello\".into()`.\nInstead of rewriting the expression to use an intermediate `let` binding, you\ncan write it as\n\n```rust\nlet bytes = \"hello\".conv::\u003cString\u003e().into_bytes();\n```\n\n## Full Functionality\n\nThe `Tap` and `Pipe` traits both provide a large number of methods, which use\ndifferent parts of the Rust language’s facilities for well-typed value access.\nRather than repeat the API documentation here, you should view the module items\nin the [documentation][docs].\n\nAs a summary, these traits provide methods that, upon receipt of a value,\n\n- apply no transformation\n- apply an `AsRef` or `AsMut` implementation\n- apply a `Borrow` or `BorrowMut` implementation\n- apply the `Deref` or `DerefMut` implementation\n\nbefore executing their effect argument.\n\nIn addition, each `Tap` method `.tap_x` has a sibling method `.tap_x_dbg` that\nperforms the same work, but only in debug builds; in release builds, the method\ncall is stripped. This allows you to leave debugging taps in your source code,\nwithout affecting your project’s performance in true usage.\n\nLastly, the `tap` module also has traits `TapOptional` and `TapFallible` which\nrun taps on the variants of `Option` and `Result` enums, respectively, and do\nnothing when the variant does not match the method name. `TapOptional::tap_some`\nhas no effect when called on a `None`, etc.\n\n\u003c!-- Badges --\u003e\n[crate]: https://crates.io/crates/tap \"Crate Link\"\n[crate_img]: https://img.shields.io/crates/v/tap.svg?logo=rust \"Crate Page\"\n[docs]: https://docs.rs/tap \"Documentation\"\n[docs_img]: https://docs.rs/tap/badge.svg \"Documentation Display\"\n[downloads_img]: https://img.shields.io/crates/dv/tap.svg?logo=rust \"Crate Downloads\"\n[license_file]: https://github.com/myrrlyn/tap/blob/master/LICENSE.txt \"License File\"\n[license_img]: https://img.shields.io/crates/l/tap.svg \"License Display\"\n[loc]: https://github.com/myrrlyn/tap \"Repository\"\n[loc_img]: https://tokei.rs/b1/github/myrrlyn/tap?category=code \"Repository Size\"\n","funding_links":[],"categories":["Libraries","库 Libraries"],"sub_categories":["Functional Programming","函数式编程 Functional Programming"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmyrrlyn%2Ftap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmyrrlyn%2Ftap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmyrrlyn%2Ftap/lists"}