├── .github ├── CODEOWNERS └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── src └── lib.rs └── tests ├── ui.rs └── ui ├── correct ├── 01_simple.rs ├── 02_associated_types.rs ├── 03_generic.rs ├── 04_lifetimes.rs ├── 05_multiple.rs ├── 06_combination.rs ├── 07_serde_hrtb.rs ├── 08_interoperability.rs ├── 09_generic_param_trait_bounds.rs └── 10_doc_comment.rs └── incorrect ├── 01_bound_failure.rs └── 01_bound_failure.stderr /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default owners for everything in the repository: 2 | * @popzxc 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '*' 7 | 8 | jobs: 9 | fmt: 10 | name: Rustfmt 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout sources 15 | uses: actions/checkout@v2 16 | 17 | - run: rustup component add clippy 18 | 19 | - name: Run rustfmt 20 | uses: actions-rs/cargo@v1 21 | with: 22 | command: fmt 23 | args: --all -- --check 24 | 25 | clippy: 26 | name: Clippy 27 | runs-on: ubuntu-latest 28 | 29 | steps: 30 | - name: Checkout sources 31 | uses: actions/checkout@v2 32 | 33 | - run: rustup component add clippy 34 | 35 | - name: Run clippy 36 | uses: actions-rs/clippy-check@v1 37 | with: 38 | token: ${{ secrets.GITHUB_TOKEN }} 39 | args: -- -D warnings 40 | 41 | deadlinks: 42 | name: Deadlinks 43 | runs-on: ubuntu-latest 44 | 45 | steps: 46 | - name: Checkout sources 47 | uses: actions/checkout@v2 48 | 49 | - name: Install rust 50 | uses: actions-rs/toolchain@v1 51 | with: 52 | toolchain: stable 53 | profile: minimal 54 | override: true 55 | 56 | - name: Install deadlinks 57 | run: cargo install cargo-deadlinks 58 | 59 | - name: Cargo doc 60 | run: cargo doc --no-deps 61 | 62 | - name: Run deadlinks 63 | run: cargo deadlinks --dir target/doc 64 | 65 | test: 66 | name: Test 67 | runs-on: ubuntu-latest 68 | 69 | steps: 70 | - name: Checkout sources 71 | uses: actions/checkout@v2 72 | 73 | - run: rustup component add clippy 74 | 75 | - name: Build 76 | uses: actions-rs/cargo@v1 77 | with: 78 | command: build 79 | args: --examples --all 80 | 81 | - name: Test 82 | uses: actions-rs/cargo@v1 83 | with: 84 | command: test 85 | args: --all 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Version 0.x.y (202x-xx-xx) 4 | 5 | ## Version 0.3.0 (2022-02-20) 6 | 7 | - Added support for doc-comments. 8 | 9 | ## Version 0.2.0 (2021-05-25) 10 | 11 | - Support for trait bounds on generic params for the alias was added. 12 | 13 | ## Version 0.1.0 (2021-04-18) 14 | 15 | - Initial crate implementation. 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trait-set" 3 | version = "0.3.0" 4 | authors = ["Igor Aleksanov "] 5 | edition = "2018" 6 | repository = "https://github.com/popzxc/trait-set" 7 | documentation = "https://docs.rs/trait-set" 8 | readme = "README.md" 9 | license = "MIT" 10 | keywords = ["trait", "alias", "helpers", "utils"] 11 | categories = ["rust-patterns"] 12 | description = "Support for trait alias feature on stable Rust." 13 | 14 | [lib] 15 | proc-macro = true 16 | 17 | [dependencies] 18 | syn = "1.0" 19 | quote = "1.0" 20 | proc-macro2 = "1.0" 21 | 22 | [dev-dependencies] 23 | trybuild = "1.0" 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Igor Aleksanov 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `trait-set`: trait aliases on stable Rust 2 | 3 | **Status:** 4 | [![CI](https://github.com/popzxc/trait-set/workflows/CI/badge.svg)](https://github.com/popzxc/trait-set/actions) 5 | 6 | **Project info:** 7 | [![Docs.rs](https://docs.rs/trait-set/badge.svg)](https://docs.rs/trait-set) 8 | [![Latest Version](https://img.shields.io/crates/v/trait-set.svg)](https://crates.io/crates/trait-set) 9 | [![License](https://img.shields.io/github/license/popzxc/trait-set.svg)](https://github.com/popzxc/trait-set) 10 | ![Rust 1.50+ required](https://img.shields.io/badge/rust-1.50+-blue.svg?label=Rust) 11 | 12 | Support for trait aliases on stable Rust. 13 | 14 | ## Description 15 | 16 | This crate provide support for [trait aliases][alias]: a feature 17 | that is already supported by Rust compiler, but is [not stable][tracking_issue] 18 | yet. 19 | 20 | The idea is simple: combine group of traits under a single name. The simplest 21 | example will be: 22 | 23 | ```rust 24 | use trait_set::trait_set; 25 | 26 | trait_set! { 27 | pub trait ThreadSafe = Send + Sync; 28 | } 29 | ``` 30 | 31 | Macro [`trait_set`] displayed here is the main entity of the crate: 32 | it allows declaring multiple trait aliases, each of them is represented 33 | as 34 | 35 | ```text 36 | [visibility] trait [AliasName][] = [Element1] + [Element2] + ... + [ElementN]; 37 | ``` 38 | 39 | [`trait_set`]: https://docs.rs/trait-set/latest/trait_set/macro.trait_set.html 40 | [alias]: https://doc.rust-lang.org/unstable-book/language-features/trait-alias.html 41 | [tracking_issue]: https://github.com/rust-lang/rust/issues/41517 42 | 43 | ## Example 44 | 45 | ```rust 46 | use trait_set::trait_set; 47 | 48 | trait_set! { 49 | // Simple combination of two traits. 50 | /// Doc-comments are also supported btw. 51 | pub trait ThreadSafe = Send + Sync; 52 | 53 | // Generic alias that gets passed to the associated type. 54 | pub trait ThreadSafeIterator = ThreadSafe + Iterator; 55 | 56 | // Specialized alias for a generic trait. 57 | pub trait ThreadSafeBytesIterator = ThreadSafeIterator; 58 | 59 | // Lifetime bounds. 60 | pub trait StaticDebug = 'static + std::fmt::Debug; 61 | 62 | // Higher-ranked trait bounds. 63 | pub trait Serde = Serialize + for<'de> Deserialize<'de>; 64 | 65 | // Lifetime as a generic parameter. 66 | pub trait SerdeLifetimeTemplate<'de> = Serialize + Deserialize<'de>; 67 | 68 | // Trait bounds on generic parameters for an alias. 69 | pub trait GenericIteratorSendableT = Iterator; 70 | } 71 | ``` 72 | 73 | ## Motivation 74 | 75 | Rust is great, and it becomes even better through time. However, a time gap between proposing 76 | a new feature and getting it stabilized is way too big. 77 | 78 | Trait aliases is a great example: 20% of functionality will serve the needs of 80%. 79 | So, until they are stabilized, this crate hopefully will allow some folks to write more readable code. 80 | 81 | ## Contributing 82 | 83 | Feel free to submit a PR! 84 | 85 | ## LICENSE 86 | 87 | `trait-set` library is licensed under the MIT License. See [LICENSE](LICENSE) for details. 88 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provide support for [trait aliases][alias]: a feature 2 | //! that is already supported by Rust compiler, but is [not stable][tracking_issue] 3 | //! yet. 4 | //! 5 | //! The idea is simple: combine group of traits under a single name. The simplest 6 | //! example will be: 7 | //! 8 | //! ```rust 9 | //! use trait_set::trait_set; 10 | //! 11 | //! trait_set! { 12 | //! pub trait ThreadSafe = Send + Sync; 13 | //! } 14 | //! ``` 15 | //! 16 | //! Macro [`trait_set`] displayed here is the main entity of the crate: 17 | //! it allows declaring multiple trait aliases, each of them is represented 18 | //! as 19 | //! 20 | //! ```text 21 | //! [visibility] trait [AliasName][] = [Element1] + [Element2] + ... + [ElementN]; 22 | //! ``` 23 | //! 24 | //! For more details, see the [`trait_set`] macro documentation. 25 | //! 26 | //! [alias]: https://doc.rust-lang.org/unstable-book/language-features/trait-alias.html 27 | //! [tracking_issue]: https://github.com/rust-lang/rust/issues/41517 28 | //! [`trait_set`]: macro.trait_set.html 29 | 30 | extern crate proc_macro; 31 | 32 | use std::iter::FromIterator; 33 | 34 | use proc_macro::TokenStream; 35 | use proc_macro2::TokenStream as TokenStream2; 36 | use quote::quote; 37 | use syn::{ 38 | parse::{Error, Parse, ParseStream}, 39 | parse_macro_input, 40 | punctuated::Punctuated, 41 | spanned::Spanned, 42 | Attribute, GenericParam, Generics, Ident, Lit, Meta, MetaNameValue, Result, Token, 43 | TypeTraitObject, Visibility, 44 | }; 45 | 46 | /// Represents one trait alias. 47 | struct TraitSet { 48 | doc_comment: Option, 49 | visibility: Visibility, 50 | _trait_token: Token![trait], 51 | alias_name: Ident, 52 | generics: Generics, 53 | _eq_token: Token![=], 54 | traits: TypeTraitObject, 55 | } 56 | 57 | impl TraitSet { 58 | /// Attempts to parse doc-comments from the trait attributes 59 | /// and returns the results as a single string. 60 | /// If multiple doc-comments were provided (e.g. with `///` and `#[doc]`), 61 | /// they will be joined with a newline. 62 | fn parse_doc(attrs: &[Attribute]) -> Result> { 63 | let mut out = String::new(); 64 | 65 | for attr in attrs { 66 | // Check whether current attribute is `#[doc = "..."]`. 67 | if let Meta::NameValue(MetaNameValue { path, lit, .. }) = attr.parse_meta()? { 68 | if let Some(path_ident) = path.get_ident() { 69 | if path_ident == "doc" { 70 | if let Lit::Str(doc_comment) = lit { 71 | out += &doc_comment.value(); 72 | // Newlines are not included in the literal value, 73 | // so we have to add them manually. 74 | out.push('\n'); 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | Ok(if !out.is_empty() { Some(out) } else { None }) 82 | } 83 | 84 | /// Renders trait alias into a new trait with bounds set. 85 | fn render(self) -> TokenStream2 { 86 | // Generic and non-generic implementation have slightly different 87 | // syntax, so it's simpler to process them individually rather than 88 | // try to generalize implementation. 89 | if self.generics.params.is_empty() { 90 | self.render_non_generic() 91 | } else { 92 | self.render_generic() 93 | } 94 | } 95 | 96 | /// Renders the trait alias without generic parameters. 97 | fn render_non_generic(self) -> TokenStream2 { 98 | let visibility = self.visibility; 99 | let alias_name = self.alias_name; 100 | let bounds = self.traits.bounds; 101 | let doc_comment = self.doc_comment.map(|val| quote! { #[doc = #val] }); 102 | quote! { 103 | #doc_comment 104 | #visibility trait #alias_name: #bounds {} 105 | 106 | impl<_INNER> #alias_name for _INNER where _INNER: #bounds {} 107 | } 108 | } 109 | 110 | /// Renders the trait alias with generic parameters. 111 | fn render_generic(self) -> TokenStream2 { 112 | let visibility = self.visibility; 113 | let alias_name = self.alias_name; 114 | let bounds = self.traits.bounds; 115 | let doc_comment = self.doc_comment.map(|val| quote! { #[doc = #val] }); 116 | 117 | // We differentiate `generics` and `bound_generics` because in the 118 | // `impl Trait` block there must be no trait bounds in the `` part, 119 | // they must go into `` part only. 120 | // E.g. `impl Trait for _INNER`. 121 | let mut unbound_generics = self.generics.clone(); 122 | for param in unbound_generics.params.iter_mut() { 123 | if let GenericParam::Type(ty) = param { 124 | if !ty.bounds.is_empty() { 125 | ty.bounds.clear(); 126 | } 127 | } 128 | } 129 | let unbound_generics = unbound_generics.params; 130 | let bound_generics = self.generics.params; 131 | 132 | // Note that it's important for `_INNER` to go *after* user-defined 133 | // generics, because generics can contain lifetimes, and lifetimes 134 | // should always go first. 135 | quote! { 136 | #doc_comment 137 | #visibility trait #alias_name<#bound_generics>: #bounds {} 138 | 139 | impl<#bound_generics, _INNER> #alias_name<#unbound_generics> for _INNER where _INNER: #bounds {} 140 | } 141 | } 142 | } 143 | 144 | impl Parse for TraitSet { 145 | fn parse(input: ParseStream) -> Result { 146 | let attrs: Vec = input.call(Attribute::parse_outer)?; 147 | let result = TraitSet { 148 | doc_comment: Self::parse_doc(&attrs)?, 149 | visibility: input.parse()?, 150 | _trait_token: input.parse()?, 151 | alias_name: input.parse()?, 152 | generics: input.parse()?, 153 | _eq_token: input.parse()?, 154 | traits: input.parse()?, 155 | }; 156 | 157 | if let Some(where_clause) = result.generics.where_clause { 158 | return Err(Error::new( 159 | where_clause.span(), 160 | "Where clause is not allowed for trait alias", 161 | )); 162 | } 163 | Ok(result) 164 | } 165 | } 166 | 167 | /// Represents a sequence of trait aliases delimited by semicolon. 168 | struct ManyTraitSet { 169 | entries: Punctuated, 170 | } 171 | 172 | impl Parse for ManyTraitSet { 173 | fn parse(input: ParseStream) -> Result { 174 | Ok(ManyTraitSet { 175 | entries: input.parse_terminated(TraitSet::parse)?, 176 | }) 177 | } 178 | } 179 | 180 | impl ManyTraitSet { 181 | fn render(self) -> TokenStream2 { 182 | TokenStream2::from_iter(self.entries.into_iter().map(|entry| entry.render())) 183 | } 184 | } 185 | 186 | /// Creates an alias for set of traits. 187 | /// 188 | /// To demonstrate the idea, see the examples: 189 | /// 190 | /// ```rust 191 | /// use trait_set::trait_set; 192 | /// 193 | /// trait_set! { 194 | /// /// Doc-comments are also supported btw. 195 | /// pub trait ThreadSafe = Send + Sync; 196 | /// pub trait ThreadSafeIterator = ThreadSafe + Iterator; 197 | /// pub trait ThreadSafeBytesIterator = ThreadSafeIterator; 198 | /// pub trait StaticDebug = 'static + std::fmt::Debug; 199 | /// } 200 | ///``` 201 | /// 202 | /// This macro also supports [higher-rank trait bound][hrtb]: 203 | /// 204 | /// ```rust 205 | /// # pub trait Serializer { 206 | /// # type Ok; 207 | /// # type Error; 208 | /// # 209 | /// # fn ok_value() -> Self::Ok; 210 | /// # } 211 | /// # pub trait Deserializer<'de> { 212 | /// # type Error; 213 | /// # } 214 | /// # 215 | /// # pub trait Serialize { 216 | /// # fn serialize(&self, serializer: S) -> Result 217 | /// # where 218 | /// # S: Serializer; 219 | /// # } 220 | /// # 221 | /// # pub trait Deserialize<'de>: Sized { 222 | /// # fn deserialize(deserializer: D) -> Result 223 | /// # where 224 | /// # D: Deserializer<'de>; 225 | /// # } 226 | /// # 227 | /// # impl Serializer for u8 { 228 | /// # type Ok = (); 229 | /// # type Error = (); 230 | /// # 231 | /// # fn ok_value() -> Self::Ok { 232 | /// # () 233 | /// # } 234 | /// # } 235 | /// # 236 | /// # impl<'de> Deserializer<'de> for u8 { 237 | /// # type Error = (); 238 | /// # } 239 | /// # 240 | /// # impl Serialize for u8 { 241 | /// # fn serialize(&self, _serializer: S) -> Result 242 | /// # where 243 | /// # S: Serializer 244 | /// # { 245 | /// # Ok(S::ok_value()) 246 | /// # } 247 | /// # } 248 | /// # 249 | /// # impl<'de> Deserialize<'de> for u8 { 250 | /// # fn deserialize(_deserializer: D) -> Result 251 | /// # where 252 | /// # D: Deserializer<'de> 253 | /// # { 254 | /// # Ok(0u8) 255 | /// # } 256 | /// # } 257 | /// use trait_set::trait_set; 258 | /// 259 | /// trait_set!{ 260 | /// pub trait Serde = Serialize + for<'de> Deserialize<'de>; 261 | /// // Note that you can also use lifetimes as a generic parameter. 262 | /// pub trait SerdeLifetimeTemplate<'de> = Serialize + Deserialize<'de>; 263 | /// } 264 | /// ``` 265 | /// 266 | /// [hrtb]: https://doc.rust-lang.org/nomicon/hrtb.html 267 | #[proc_macro] 268 | pub fn trait_set(tokens: TokenStream) -> TokenStream { 269 | let input = parse_macro_input!(tokens as ManyTraitSet); 270 | input.render().into() 271 | } 272 | -------------------------------------------------------------------------------- /tests/ui.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn ui_pass() { 3 | let t = trybuild::TestCases::new(); 4 | t.pass("tests/ui/correct/*.rs"); 5 | } 6 | 7 | #[test] 8 | fn ui_fail() { 9 | let t = trybuild::TestCases::new(); 10 | t.compile_fail("tests/ui/incorrect/*.rs"); 11 | } 12 | -------------------------------------------------------------------------------- /tests/ui/correct/01_simple.rs: -------------------------------------------------------------------------------- 1 | //! Tests the simplest possible combination of traits. 2 | 3 | use trait_set::trait_set; 4 | 5 | trait_set! { 6 | pub(crate) trait TraitSet = Send + Sync; 7 | } 8 | 9 | fn test_set(_arg: T) {} 10 | 11 | fn main() { 12 | test_set(10u8); 13 | test_set("hello"); 14 | } 15 | -------------------------------------------------------------------------------- /tests/ui/correct/02_associated_types.rs: -------------------------------------------------------------------------------- 1 | //! Checks that traits with associated types can be used in alias. 2 | 3 | use trait_set::trait_set; 4 | 5 | trait_set! { 6 | pub(crate) trait BytesIterator = Iterator; 7 | } 8 | 9 | fn test_set(_arg: T) {} 10 | 11 | fn main() { 12 | test_set([10u8, 20, 30].as_ref().iter().copied()); 13 | test_set(b"abcde".iter().copied()); 14 | } 15 | -------------------------------------------------------------------------------- /tests/ui/correct/03_generic.rs: -------------------------------------------------------------------------------- 1 | //! Checks that generic aliases are processed as expected. 2 | 3 | use trait_set::trait_set; 4 | 5 | pub trait GenericTrait { 6 | fn new(t: T) -> Self; 7 | } 8 | 9 | impl GenericTrait for u8 { 10 | fn new(t: u8) -> u8 { 11 | t 12 | } 13 | } 14 | 15 | trait_set! { 16 | pub(crate) trait GenericIterator = Iterator; 17 | pub(crate) trait GenericFoo = GenericTrait; 18 | pub(crate) trait SpecializedFoo = GenericTrait; 19 | } 20 | 21 | fn test_set>(_arg: T) {} 22 | fn test_generic>(_arg: T) {} 23 | fn test_specialized(_arg: T) {} 24 | 25 | fn main() { 26 | test_set([10u8, 20, 30].as_ref().iter().copied()); 27 | test_set(b"abcde".iter().copied()); 28 | test_generic(10); 29 | test_specialized(10); 30 | } 31 | -------------------------------------------------------------------------------- /tests/ui/correct/04_lifetimes.rs: -------------------------------------------------------------------------------- 1 | //! Checks that lifetime bounds are accepted. 2 | 3 | use trait_set::trait_set; 4 | 5 | trait_set! { 6 | pub(crate) trait Set = 'static + Send + Sync; 7 | } 8 | 9 | fn test_set(_arg: T) {} 10 | 11 | fn main() { 12 | test_set([10u8, 20, 30].as_ref().iter().copied()); 13 | test_set(b"abcde".iter().copied()); 14 | } 15 | -------------------------------------------------------------------------------- /tests/ui/correct/05_multiple.rs: -------------------------------------------------------------------------------- 1 | //! Checks that multiple aliases can exist within one `trait_set` call. 2 | 3 | use trait_set::trait_set; 4 | 5 | trait_set! { 6 | pub(crate) trait TraitSet = Send + Sync; 7 | pub trait BytesIterator = Iterator; 8 | trait GenericIterator = Iterator; 9 | } 10 | 11 | fn test_set(_arg: T) {} 12 | fn test_iter(_arg: T) {} 13 | fn test_generic_iter>(_arg: T) {} 14 | 15 | fn main() { 16 | test_set(10u8); 17 | test_iter([10u8, 20, 30].as_ref().iter().copied()); 18 | test_generic_iter([10u8, 20, 30].as_ref().iter().copied()); 19 | } 20 | -------------------------------------------------------------------------------- /tests/ui/correct/06_combination.rs: -------------------------------------------------------------------------------- 1 | //! Checks that aliases can be combined between each other into a new alias. 2 | 3 | use trait_set::trait_set; 4 | 5 | trait_set! { 6 | pub trait ThreadSafe = Send + Sync; 7 | pub trait BytesIterator = Iterator; 8 | pub trait ThreadSafeBytesIterator = ThreadSafe + BytesIterator; 9 | } 10 | 11 | fn test_set(_arg: T) {} 12 | 13 | fn main() { 14 | test_set([10u8, 20, 30].as_ref().iter().copied()); 15 | } 16 | -------------------------------------------------------------------------------- /tests/ui/correct/07_serde_hrtb.rs: -------------------------------------------------------------------------------- 1 | //! Checks the complex serde-inspired example with 2 | //! higher-ranked trait bounds. 3 | 4 | use trait_set::trait_set; 5 | 6 | pub trait Serializer { 7 | type Ok; 8 | type Error; 9 | 10 | fn ok_value() -> Self::Ok; 11 | } 12 | pub trait Deserializer<'de> { 13 | type Error; 14 | } 15 | 16 | pub trait Serialize { 17 | fn serialize(&self, serializer: S) -> Result 18 | where 19 | S: Serializer; 20 | } 21 | 22 | pub trait Deserialize<'de>: Sized { 23 | fn deserialize(deserializer: D) -> Result 24 | where 25 | D: Deserializer<'de>; 26 | } 27 | 28 | impl Serializer for u8 { 29 | type Ok = (); 30 | type Error = (); 31 | 32 | fn ok_value() -> Self::Ok { 33 | () 34 | } 35 | } 36 | 37 | impl<'de> Deserializer<'de> for u8 { 38 | type Error = (); 39 | } 40 | 41 | impl Serialize for u8 { 42 | fn serialize(&self, _serializer: S) -> Result 43 | where 44 | S: Serializer, 45 | { 46 | Ok(S::ok_value()) 47 | } 48 | } 49 | 50 | impl<'de> Deserialize<'de> for u8 { 51 | fn deserialize(_deserializer: D) -> Result 52 | where 53 | D: Deserializer<'de>, 54 | { 55 | Ok(0u8) 56 | } 57 | } 58 | 59 | trait_set! { 60 | pub trait Serde = Serialize + for<'de> Deserialize<'de>; 61 | pub trait SerdeLifetimeTemplate<'de> = Serialize + Deserialize<'de>; 62 | } 63 | 64 | fn test_set(_arg: T) {} 65 | fn test_template<'de, T: SerdeLifetimeTemplate<'de>>(_arg: T) {} 66 | 67 | fn main() { 68 | test_set(0u8); 69 | test_template(0u8); 70 | } 71 | -------------------------------------------------------------------------------- /tests/ui/correct/08_interoperability.rs: -------------------------------------------------------------------------------- 1 | //! Checks that aliases for the same set are interoperable between 2 | //! each other and with plain trait combination. 3 | 4 | use trait_set::trait_set; 5 | 6 | trait_set! { 7 | pub(crate) trait TraitSet1 = Send + Sync; 8 | pub(crate) trait TraitSet2 = Send + Sync; 9 | } 10 | 11 | fn test_set1(arg: T) { 12 | test_set2(arg) 13 | } 14 | fn test_set2(arg: T) { 15 | test_set3(arg) 16 | } 17 | fn test_set3(_arg: T) {} 18 | 19 | fn main() { 20 | test_set1(10u8); 21 | test_set1("hello"); 22 | } 23 | -------------------------------------------------------------------------------- /tests/ui/correct/09_generic_param_trait_bounds.rs: -------------------------------------------------------------------------------- 1 | //! Checks that trait bounds can be applied to the generic arguments 2 | //! of an alias. 3 | 4 | use trait_set::trait_set; 5 | 6 | pub trait GenericTrait { 7 | fn new(t: T) -> Self; 8 | } 9 | 10 | impl GenericTrait for u8 { 11 | fn new(t: u8) -> u8 { 12 | t 13 | } 14 | } 15 | 16 | trait_set! { 17 | pub(crate) trait GenericIteratorSendableT = Iterator; 18 | } 19 | 20 | fn test_set>(_arg: T) {} 21 | 22 | fn main() { 23 | test_set([10u8, 20, 30].as_ref().iter().copied()); 24 | test_set(b"abcde".iter().copied()); 25 | } 26 | -------------------------------------------------------------------------------- /tests/ui/correct/10_doc_comment.rs: -------------------------------------------------------------------------------- 1 | //! Tests that adding doc-comment doesn't break the build. 2 | 3 | use trait_set::trait_set; 4 | 5 | trait_set! { 6 | /// This is a doc-comment! 7 | /// 8 | /// It has multiple lines! 9 | /// 10 | #[doc = "And it's mixed with different flavors of comments..."] 11 | /** Even block-comments, */ 12 | pub(crate) trait TraitSet = Send + Sync; 13 | } 14 | 15 | fn test_set(_arg: T) {} 16 | 17 | fn main() { 18 | test_set(10u8); 19 | test_set("hello"); 20 | } 21 | -------------------------------------------------------------------------------- /tests/ui/incorrect/01_bound_failure.rs: -------------------------------------------------------------------------------- 1 | //! Checks that trait violation is correctly rendered in error. 2 | 3 | use std::cell::RefCell; 4 | use trait_set::trait_set; 5 | 6 | trait_set! { 7 | pub trait ThreadSafe = Send + Sync; 8 | } 9 | 10 | fn test(_t: T) {} 11 | 12 | fn main() { 13 | test(RefCell::new(10u8)); 14 | } 15 | -------------------------------------------------------------------------------- /tests/ui/incorrect/01_bound_failure.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: `RefCell` cannot be shared between threads safely 2 | --> $DIR/01_bound_failure.rs:13:10 3 | | 4 | 13 | test(RefCell::new(10u8)); 5 | | ---- ^^^^^^^^^^^^^^^^^^ `RefCell` cannot be shared between threads safely 6 | | | 7 | | required by a bound introduced by this call 8 | | 9 | = help: the trait `Sync` is not implemented for `RefCell` 10 | note: required because of the requirements on the impl of `ThreadSafe` for `RefCell` 11 | --> $DIR/01_bound_failure.rs:6:1 12 | | 13 | 6 | / trait_set! { 14 | 7 | | pub trait ThreadSafe = Send + Sync; 15 | | | ^^^^^^^^^^ 16 | 8 | | } 17 | | |_^ 18 | note: required by a bound in `test` 19 | --> $DIR/01_bound_failure.rs:10:12 20 | | 21 | 10 | fn test(_t: T) {} 22 | | ^^^^^^^^^^ required by this bound in `test` 23 | = note: this error originates in the macro `trait_set` (in Nightly builds, run with -Z macro-backtrace for more info) 24 | --------------------------------------------------------------------------------