From 91c46d1a3314b326827fbe14e12f3602024196d7 Mon Sep 17 00:00:00 2001 From: "Dongjia \"toka\" Zhang" Date: Mon, 17 Feb 2025 17:00:23 +0100 Subject: [PATCH] Update CONTRIBUTING.md to forbid cyclic dependency (#2999) --- CONTRIBUTING.md | 64 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a8cc60e368..a7477a9636 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 -## Pull Request guideline +## Pull Request Guideline Even though we will gladly assist you in finishing up your PR, try to: @@ -14,7 +14,7 @@ Even though we will gladly assist you in finishing up your PR, try to: Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. -### Pre-commit hooks +### Pre-commit Hooks Some of these checks can be performed automatically during commit using [pre-commit](https://pre-commit.com/). Once the package is installed, simply run `pre-commit install` to enable the hooks, the checks will run automatically before the commit becomes effective. @@ -27,7 +27,10 @@ Before making your pull requests, try to see if your code follows these rules. - `PhantomData` should have the smallest set of types needed. Try not adding `PhantomData` to your struct unless it is really necessary. Also even when you really need `PhantomData`, try to keep the types `T` used in `PhantomData` as smallest as possible - Wherever possible, trait implementations with lifetime specifiers should use '_ lifetime elision. - Complex constructors should be replaced with `typed_builder`, or write code in the builder pattern for yourself. -- Remove generic restrictions at the definitions (e.g., we do not need to specify that types impl `Serialize`, `Deserialize`, or `Debug` anymore at the struct definitions). Therefore, try avoiding code like this unless the constraint is really necessary. + + +## Rules for Generics and Associated Types +1. Remove generic restrictions at the definitions (e.g., we do not need to specify that types impl `Serialize`, `Deserialize`, or `Debug` anymore at the struct definitions). Therefore, try avoiding code like this unless the constraint is really necessary. ```rust pub struct X where @@ -36,7 +39,7 @@ pub struct X fn ... } ``` -- Reduce generics to the least restrictive necessary. __Never overspecify the constraints__. There's no automated tool to check the useless constraints, so you have to verify this manually. +2. Reduce generics to the least restrictive necessary. __Never overspecify the constraints__. There's no automated tool to check the useless constraints, so you have to verify this manually. ```rust pub struct X where @@ -46,8 +49,7 @@ pub struct X } ``` -- Prefer generic to associated types in traits definition as much as possible. They are much easier to use around, and avoid tricky caveats / type repetition in the code. It is also much easier to have unconstrained struct definitions. - +3. Prefer generic to associated types in traits definition as much as possible. They are much easier to use around, and avoid tricky caveats / type repetition in the code. It is also much easier to have unconstrained struct definitions. Try not to write this: ```rust pub trait X @@ -65,7 +67,7 @@ pub trait X } ``` -- Traits which have an associated type (if you have made sure you cannot use a generic instead) should refer to the associated type, not the concrete/generic. In other words, you should only have the associated type when you can define a getter to it. For example, in the following code, you can define a associate type. +4. Traits which have an associated type (if you have made sure you cannot use a generic instead) should refer to the associated type, not the concrete/generic. In other words, you should only have the associated type when you can define a getter to it. For example, in the following code, you can define a associate type. ```rust pub trait X { @@ -74,17 +76,7 @@ pub trait X fn a(&self) -> Self::A; } ``` - -- __Ideally__ the types used in the arguments of methods in traits should have the same as the types defined on the traits. -```rust -pub trait X // <- this trait have 3 generics, A, B, and C -{ - fn do_stuff(&self, a: A, b: B, c: C); // <- this is good because it uses all A, B, and C. - - fn do_other_stuff(&self, a: A, b: B); // <- this is not ideal because it does not have C. -} -``` -- Generic naming should be consistent. Do NOT use multiple name for the same generic, it just makes things more confusing. Do: +5. Generic naming should be consistent. Do NOT use multiple name for the same generic, it just makes things more confusing. Do: ```rust pub struct X { phantom: PhanomData, @@ -100,7 +92,38 @@ pub struct X { impl X {} // <- Do NOT do that, use A instead of B ``` -- Always alphabetically order the type generics. Therefore, +6. __Ideally__ the types used in the arguments of methods in traits should have the same as the types defined on the traits. +```rust +pub trait X // <- this trait have 3 generics, A, B, and C +{ + fn do_stuff(&self, a: A, b: B, c: C); // <- this is good because it uses all A, B, and C. + + fn do_other_stuff(&self, a: A, b: B); // <- this is not ideal because it does not have C. +} +``` +7. Try to avoid cyclical dependency if possible. Sometimes it is necessary but try to avoid it. For example, The following code is a bad example. +```rust +pub struct X {} +pub struct Y {} + +pub trait Fuzzer: Sized { + fn fuzz(&self, em: &EM) + where + EM: EventManager + { + em.do_stuff(self); + } +} + +pub trait EventManager: Sized { + fn do_stuff(&self, fuzzer: &Z); // <- This function signature should not take fuzzer +} +``` +trait `EventManager` should not implement any method that takes fuzzer, any object that could implement `Fuzzer` trait. + + +## Formatting +1. Always alphabetically order the type generics. Therefore, ```rust pub struct X {}; // <- Generics are alphabetically ordered ``` @@ -108,7 +131,8 @@ But not, ```rust pub struct X {}; // <- Generics are not ordered ``` -- Similarly, generic bounds in `where` clauses should be alphabetically sorted. Prefer: +2. Similarly, generic bounds in `where` clauses should be alphabetically sorted. +Prefer: ```rust pub trait FooA {} pub trait FooB {}