
Rust 매크로와 Cargo 하위 명령어로 afl.rs를 통한 퍼징을 자동화하는 도구로, 코퍼스 생성과 하네스 구현을 포함하며 Rust의 테스트 프레임워크와 통합됩니다.
test-fuzz는 afl.rs를 사용한 퍼징과 관련된 특정 작업을 자동화하는 Cargo 하위 명령이자 Rust 매크로 모음입니다. 여기에는 다음이 포함됩니다:
test-fuzz는 Rust의 테스트 기능을 사용하여 (부분적으로) 이러한 작업을 수행합니다. 예를 들어, 퍼징 코퍼스를 생성하기 위해 test-fuzz는 cargo test 실행 중 대상이 호출될 때마다 해당 대상의 인수를 기록합니다. 마찬가지로 test-fuzz는 cargo-test로 생성된 바이너리에서 퍼징 하니스를 추가 테스트로 구현합니다. Rust 테스트 기능과의 이러한 긴밀한 통합이 test-fuzz라는 이름의 동기가 되었습니다.
목차
test_fuzz 매크로]test_fuzz_impl 매크로]cargo test-fuzz 명령]test-fuzz 패키지 기능]다음 명령으로 cargo-test-fuzz와 afl.rs를 설치하세요:```sh
cargo install cargo-test-fuzz cargo-afl
## 개요
`test-fuzz`를 사용한 퍼징은 기본적으로 세 단계로 이루어집니다:\*
1. **퍼징 타깃을 식별하세요**:
- 대상 크레이트의 `Cargo.toml` 파일에 다음 `dependencies`를 추가하세요:
```toml
serde = "*"
test-fuzz = "*"
```
- 대상 함수 앞에 [`test_fuzz`] 매크로를 추가하세요:
```rust
#[test_fuzz::test_fuzz]
fn foo(...) {
...
}
```
2. `cargo test`를 실행하여 **코퍼스를 생성하세요**: ```
cargo test
cargo test-fuzz를 실행하여 대상을 퍼징하세요: ```
cargo test-fuzz foo
* 재부팅 후 추가적인 사전 단계가 필요할 수 있습니다:```sh cargo afl system-config
위 명령은 내부적으로 `sudo`를 실행합니다. 따라서 비밀번호를 입력하라는 메시지가 표시될 수 있습니다.
## 구성 요소
### `test_fuzz` 매크로
함수 앞에 `test_fuzz` 매크로를 붙이면 해당 함수가 퍼즈 타깃(fuzz target)임을 나타냅니다.
`test_fuzz` 매크로의 주요 효과는 다음과 같습니다.
- 대상이 호출될 때마다 인수를 직렬화하여 코퍼스(corpus) 파일에 기록하도록 대상에 계측(instrumentation)을 추가합니다. 이 계측은 `#[cfg(test)]`로 보호되므로 테스트 실행 중에만 코퍼스 파일이 생성됩니다(단, 아래 [`enable_in_production`] 참조).
- 표준 입력에서 인수를 읽고 역직렬화하여 대상에 적용하는 테스트를 추가합니다. 이 테스트는 [`cargo test-fuzz`]가 설정한 환경 변수를 확인하므로, 일반적인 `cargo test` 실행 중에는 표준 입력을 읽기 위해 블로킹하지 않습니다. 이름 충돌 가능성을 줄이기 위해 테스트는 모듈 안에 포함됩니다. 현재 모듈 이름은 `target_fuzz`이며, 여기서 `target`은 대상의 이름입니다(단, 아래 [`rename`] 참조).
#### 인자
##### `bounds = "where_predicates"`
인수를 직렬화/역직렬화하는 데 사용되는 구조체에 `where_predicates`(예: 트레이트 바운드)를 적용합니다. 예를 들어 대상의 인수 타입이 연관 타입(associated type)인 경우 필요할 수 있습니다. 예시는 이 저장소의 [associated_type.rs]를 참조하세요.
##### `generic_args = "parameters"`
퍼징할 때 대상의 타입 매개변수로 `parameters`를 사용합니다. 예시:```rust
#[test_fuzz(generic_args = "String")]
fn foo<T: Clone + Debug + Serialize>(x: &T) {
...
}
참고: 대상의 인자는 타입 매개변수의 모든 인스턴스화에 대해 직렬화 가능해야 합니다. 그러나 대상의 인자가 역직렬화 가능해야 하는 것은 대상이 parameters로 인스턴스화될 때뿐입니다.
impl_generic_args = "parameters"퍼징 시 parameters를 대상의 Self 타입 매개변수로 사용하세요. 예:```rust
#[test_fuzz_impl]
impl<T: Clone + Debug + Serialize> for Foo {
#[test_fuzz(impl_generic_args = "String")]
fn bar(&self, x: &T) {
...
}
}
참고: 대상의 인자는 `Self` 타입 매개변수의 **모든** 인스턴스화에 대해 직렬화 가능해야 합니다. 그러나 대상의 인자가 역직렬화 가능해야 하는 것은 대상의 `Self`가 `parameters`로 인스턴스화될 때뿐입니다.
##### `convert = "X, Y"`
대상의 인자를 직렬화할 때, `Y`의 `From<X>` 구현을 사용하여 타입 `X`의 값을 타입 `Y`로 변환하거나, 비표준 트레이트 `test_fuzz::FromRef<X>`에 대한 `Y`의 구현을 사용하여 타입 `&X`의 값을 타입 `Y`로 변환합니다. 역직렬화할 때는 비표준 트레이트 `test_fuzz::Into<X>`에 대한 `Y`의 구현을 사용하여 해당 값을 타입 `X`로 다시 변환합니다.
즉, `convert = "X, Y"`를 사용하려면 특정 구현이 함께 있어야 합니다. `X`가 [`Clone`]을 구현한다면, `Y`는 다음을 구현할 수 있습니다:```rust
impl From<X> for Y {
fn from(x: X) -> Self {
...
}
}
만약 X가 Clone을 구현하지 않는다면, Y는 다음을 구현해야 합니다:```rust
impl test_fuzz::FromRef for Y {
fn from_ref(x: &X) -> Self {
...
}
}
추가로, `Y`는 (`X`가 [`Clone`]을 구현하는지 여부와 관계없이) 다음을 구현해야 합니다:```rust
impl test_fuzz::Into<X> for Y {
fn into(self) -> X {
...
}
}
test_fuzz::Into의 정의는 std::convert::Into의 정의와 동일합니다. 표준이 아닌 트레이트를 사용하는 이유는 표준 트레이트의 포괄 구현(blanket implementations)에서 발생할 수 있는 충돌을 피하기 위해서입니다.
enable_in_production테스트를 실행 중이 아닐 때도 TEST_FUZZ_WRITE 환경 변수가 설정되어 있으면 코퍼스 파일을 생성합니다. 기본값은 TEST_FUZZ_WRITE 설정 여부와 관계없이 테스트 실행 시에만 코퍼스 파일을 생성하는 것입니다. 패키지 디렉터리 밖에서 타깃을 실행하는 경우 TEST_FUZZ_MANIFEST_PATH를 패키지의 Cargo.toml 파일 경로로 설정하세요.
경고: enable_in_production을 설정하면 서비스 거부(denial-of-service) 벡터가 발생할 수 있습니다. 예를 들어, 다양한 인수로 여러 번 호출되는 함수에 이 옵션을 설정하면 디스크가 가득 찰 수 있습니다. TEST_FUZZ_WRITE 검사는 이러한 가능성에 대한 일부 방어를 제공하기 위한 것입니다. 그럼에도 불구하고 이 옵션을 사용하기 전에 신중히 고려하십시오.
execute_with = "function"타깃을 직접 호출하는 대신:
FnOnce() -> R 유형의 클로저를 구성합니다. 여기서 R은 타깃의 반환 유형이며, 클로저를 호출하면 타깃이 호출됩니다.function을 호출합니다.이렇게 타깃을 호출하면 function이 호출 환경을 설정할 수 있습니다. 예를 들어 Substrate externalities를 퍼징할 때 유용할 수 있습니다.
no_auto_generate타깃에 대해 auto-generate corpus files를 시도하지 않습니다.
only_generic_args테스트 실행 중 타깃의 제네릭 인자를 기록하되, 코퍼스 파일을 생성하지 않고 퍼징 하네스도 구현하지 않습니다. 이는 타깃이 제네릭 함수인데 퍼징에 사용할 타입 매개변수가 무엇인지 불분명할 때 유용할 수 있습니다.
의도된 워크플로는 다음과 같습니다: only_generic_args를 활성화한 다음 cargo test를 실행하고 이어서 cargo test-fuzz --display generic-args를 실행합니다. 결과로 나온 제네릭 인자 중 하나가 generic_args의 parameters로 사용할 수 있을 수 있습니다. 마찬가지로 cargo test-fuzz --display impl-generic-args의 결과로 나온 제네릭 인자는 impl_generic_args의 parameters로 사용할 수 있을 수 있습니다.
그러나 테스트 중 특정 매개변수로 타깃이 호출되었다고 해서 해당 매개변수를 사용할 때 타깃의 인수가 직렬화/역직렬화 가능하다는 의미는 아닙니다. --display generic-args/--display impl-generic-args의 결과는 단지 참고용일 뿐입니다.
rename = "name"타깃을 둘러싼 범위에 모듈을 추가할 때 타깃의 이름이 name인 것처럼 취급합니다. test_fuzz 매크로의 확장은 둘러싼 범위에 모듈 정의를 추가합니다. 기본적으로 모듈 이름은 다음과 같이 지정됩니다:
impl 블록에 나타나지 않으면 모듈 이름은 target_fuzz__이며, 여기서 target은 타깃의 이름입니다.impl 블록에 나타나면 모듈 이름은 path_target_fuzz__이며, 여기서 path는 impl의 Self 유형 경로의 마지막 세그먼트입니다.그러나 이 옵션을 사용하면 모듈 이름이 대신 name_fuzz__가 됩니다. 예:```rust
#[test_fuzz(rename = "bar")]
fn foo() {}
// Without the use of rename, a name collision and compile error would result.
mod foo_fuzz__ {}
#### 함수 인자에 대한 Serde 필드 속성
`test_fuzz` 매크로는 [Serde 필드 속성]을 함수 인자에 적용할 수 있게 해줍니다. 이는 다루기 어려운 타입을 처리하는 또 다른 도구를 제공합니다.
다음은 예시입니다. `Context`는 `Mutex`를 포함하기 때문에 `serde::Serialize` 및 `serde::Deserialize` 트레이트를 파생할 수 없습니다. 그러나 `Context`는 `Default`를 구현합니다. 따라서 `Context` 인자에 `#[serde(skip)]`을 적용하면 직렬화할 때 해당 인자가 생략되고, 역직렬화할 때 기본값을 사용하게 됩니다.```rust
use std::sync::Mutex;
// Traits `serde::Serialize` and `serde::Deserialize` cannot be derived for `Context` because it
// contains a `Mutex`.
#[derive(Default)]
struct Context {
lock: Mutex<()>,
}
impl Clone for Context {
fn clone(&self) -> Self {
Self {
lock: Mutex::new(()),
}
}
}
#[test_fuzz::test_fuzz]
fn target(#[serde(skip)] context: Context, x: i32) {
assert!(x >= 0);
}
Serde 필드 속성이 인자에 적용되면 test_fuzz 매크로는 해당 인자에 대해 다른 [변환]을 수행하지 않습니다.
test_fuzz_impl 매크로test_fuzz 매크로가 impl 블록에서 사용될 때마다,
impl 앞에 반드시 test_fuzz_impl 매크로가 와야 합니다. 예시:```rust
#[test_fuzz_impl]
impl Foo {
#[test_fuzz]
fn bar(&self, x: &str) {
...
}
}
이 요구 사항의 이유는 다음과 같습니다. `test_fuzz` 매크로의 확장은 포함하는 범위에 모듈 정의를 추가합니다. 그러나 모듈 정의는 `impl` 블록 내부에 나타날 수 없습니다. `impl` 앞에 `test_fuzz_impl` 매크로를 배치하면 모듈이 `impl` 블록 외부에 추가됩니다.
다음과 같은 오류가 표시된다면 `test_fuzz_impl` 매크로 사용이 누락되었음을 의미할 가능성이 높습니다:```
error: module is not supported in `trait`s or `impl`s
test_fuzz_impl 현재 옵션이 없습니다.
cargo test-fuzz 명령cargo test-fuzz 명령은 퍼즈 타겟과 상호 작용하고 해당 코퍼스(corpora), 크래시(crashes), 행(hangs), 작업 큐(work queues)를 조작하는 데 사용됩니다. 호출 예는 다음과 같습니다:
foo의 코퍼스 표시 ```
cargo test-fuzz foo --display corpus
foo ```
cargo test-fuzz foo
foo에 대해 발견된 크래시 재생 ```
cargo test-fuzz foo --replay crashes
Usage: cargo test-fuzz [OPTIONS] [TARGETNAME] [-- ...]
Arguments: [TARGETNAME] String that fuzz target's name must contain [ARGS]... Arguments for the fuzzer
Options:
--backtrace Display backtraces
--consolidate Move one target's crashes, hangs, and work queue to its corpus; to
consolidate all targets, use --consolidate-all
--coverage Generate coverage for corpus, crashes, hangs, or work queue. Note
that generating coverage for instrumented fuzz targets is not
supported.
--cpus Fuzz using at most cpus; default is all but one
--display Display corpus, crashes, generic args, impl generic args, hangs,
or work queue. By default, an uninstrumented fuzz target is used.
To display with instrumentation, append -instrumented to
, e.g., --display corpus-instrumented.
--exact Target name is an exact name rather than a substring
--exit-code Exit with 0 if the time limit was reached, 1 for other
programmatic aborts, and 2 if an error occurred; implies --no-ui,
does not imply --run-until-crash or --max-total-time
--features Space or comma separated list of features to activate
--list List fuzz targets
--manifest-path Path to Cargo.toml
--max-total-time Fuzz at most of time (equivalent to -- -V )
--no-default-features Do not activate the default feature
--no-run Compile, but don't fuzz
--no-ui Disable user interface
-p, --package Package containing fuzz target
--persistent Enable persistent mode fuzzing
--pretty Pretty-print debug output when generating coverage, displaying, or
replaying
--release Build in release mode
--replay Replay corpus, crashes, hangs, or work queue. By default, an
uninstrumented fuzz target is used. To replay with
instrumentation, append -instrumented to , e.g.,
--replay corpus-instrumented.
--reset Clear fuzzing data for one target, but leave corpus intact; to
reset all targets, use --reset-all
--resume Resume target's last fuzzing session
--run-until-crash Stop fuzzing once a crash is found
--slice If there are not sufficiently many cpus to fuzz all targets
simultaneously, fuzz them in intervals of [default:
1200]
--test Integration test containing fuzz target
--timeout Number of seconds to consider a hang when fuzzing or replaying
(equivalent to -- -t <TIMEOUT * 1000> when fuzzing)
--verbose Show build output when generating coverage, displaying, or
replaying
-h, --help Print help
-V, --version Print version
Try cargo afl fuzz --help to see additional fuzzer options.
`--display` 옵션을 사용하면 대상 프로그램이 stderr에 출력하는 모든 내용이 표시됩니다. 여기에는 `eprintln!` 문의 출력뿐만 아니라 `dbg!`와 같은 디버깅 매크로의 출력도 포함됩니다. 이는 특정 입력을 처리할 때 코드에서 어떤 일이 일어나고 있는지 이해하는 데 유용할 수 있습니다.
`--display` 및 `--replay` 옵션은 함께 전달할 수 있으므로 단일 명령으로 코퍼스 항목을 보고 재생할 수 있습니다. 예를 들면 다음과 같습니다.```
cargo test-fuzz foo --display corpus --replay corpus
Warning: These utilties are excluded from semantic versioning and may be removed in future versions of test-fuzz.
dont_care!The dont_care! macro can be used to implement serde::Serialize/serde::Deserialize for types that are easy to construct and whose values you do not care to record. Intuitively, dont_care!($ty, $expr) says:
$ty when serializing.$ty with $expr when deserializing.More specifically, dont_care!($ty, $expr) expands to the following:```rust
impl serde::Serialize for $ty {
fn serialize(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
().serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> std::result::Result<Self, D::Error> where D: serde::Deserializer<'de>, { <()>::deserialize(deserializer).map(|_| $expr) } }
If `$ty` is a unit struct, then `$expr` can be omitted. That is, `dont_care!($ty)` is equivalent to `dont_care!($ty, $ty)`.
#### `leak!`
The `leak!` macro can help to serialize target arguments that are references and whose types implement the [`ToOwned`] trait. It is meant to be used with the [`convert`] option.
Specifically, an invocation of the following form declares a type `LeakedX`, and implements the `From` and `test_fuzz::Into` traits for it:```rust
leak!(X, LeakedX);
그런 다음 convert 옵션과 함께 LeakedX를 다음과 같이 사용할 수 있습니다:```rust
#[test_fuzz::test_fuzz(convert = "&X, LeakedX")
`X`가 [`Path`]인 예는 이 저장소의 [conversion.rs]에 나타납니다.
보다 일반적으로, `leak!($ty, $ident)` 형태의 호출은 다음과 같이 확장됩니다:```rust
#[derive(Clone, std::fmt::Debug, serde::Deserialize, serde::Serialize)]
struct $ident(<$ty as ToOwned>::Owned);
impl From<&$ty> for $ident {
fn from(ty: &$ty) -> Self {
Self(ty.to_owned())
}
}
impl test_fuzz::Into<&$ty> for $ident {
fn into(self) -> &'static $ty {
Box::leak(Box::new(self.0))
}
}
serialize_ref / deserialize_refserialize_ref와 deserialize_ref는 leak!와 유사하게 동작하지만, Serde의 serialize_with 및 deserialize_with 필드 속성과 함께 사용하기 위한 것입니다(각각).```rust
fn serialize_ref<S, T>(x: &&T, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
T: serde::Serialize,
{
::serialize(*x, serializer)
}
fn deserialize_ref<'de, D, T>(deserializer: D) -> Result<&'static T, D::Error> where D: serde::Deserializer<'de>, T: serde:🇩🇪:DeserializeOwned + std::fmt::Debug, { let x = ::deserialize(deserializer)?; Ok(Box::leak(Box::new(x))) }
#### `serialize_ref_mut` / `deserialize_ref_mut`
`serialize_ref_mut` 및 `deserialize_ref_mut`는 (각각) `serialize_ref` 및 `deserialize_ref`와 유사하지만, 불변 참조 대신 가변 참조에 대해 동작합니다.
## `test-fuzz` 패키지 기능
이 섹션의 기능은 `test-fuzz` 패키지 전체에 적용됩니다. [The Cargo Book]에 설명된 대로 `test-fuzz`의 종속성 지정에서 이 기능들을 활성화하십시오. 예를 들어, `cast_checks` 기능을 활성화하려면 다음을 사용하세요:```toml
test-fuzz = { version = "*", features = ["cast_checks"] }
test-fuzz 패키지는 현재 다음 기능을 지원합니다:
cast_checkscast_checks를 사용하여 대상 함수에 잘못된 캐스트가 있는지 자동으로 검사하세요.
이 기능은 test_fuzz macro가 적용된 함수에 대해서만 cast_checks를 활성화하며, 해당 함수가 호출하는 함수에는 활성화하지 않습니다.
test-fuzz는 대상 인수를 여러 Serde 형식으로 직렬화할 수 있습니다. 다음은 형식을 선택하는 데 사용되는 기능입니다.
cargo-test-fuzz는 특정 트레이트를 구현하는 타입에 대한 값을 자동 생성할 수 있습니다. 대상 함수의 모든 인수 타입이 이러한 트레이트를 구현한다면, cargo-test-fuzz는 해당 대상에 대한 코퍼스 파일을 자동 생성할 수 있습니다.
cargo-test-fuzz가 현재 지원하는 트레이트와 각각에 대해 생성되는 값은 다음과 같습니다:
범례
Add - core::ops::AddBounded - num_traits::bounds::BoundedDefault - std::default::DefaultDiv - core::ops::DivOne - num_traits::OneSub - core::ops::SubTEST_FUZZ_LOG매크로 확장 중에:
TEST_FUZZ_LOG이 1로 설정된 경우, 계측된 모든 퍼즈 타깃과 모듈 정의를 표준 출력에 기록합니다.TEST_FUZZ_LOG이 크레이트 이름으로 설정된 경우, 해당 크레이트의 계측된 퍼즈 타깃과 모듈 정의를 표준 출력에 기록합니다.이는 디버깅에 유용할 수 있습니다.
TEST_FUZZ_MANIFEST_PATH타깃을 패키지 디렉터리 밖에서 실행할 때, 이 위치에서 패키지의 Cargo.toml 파일을 찾습니다. enable_in_production을 사용하는 경우 이 환경 변수를 설정해야 할 수 있습니다.
TEST_FUZZ_WRITEenable_in_production이 설정된 대상의 경우, 테스트를 실행하지 않을 때에도 코퍼스 파일을 생성합니다.
대상 함수의 인수는 Clone 트레이트를 구현해야 합니다. 이 요구 사항이 있는 이유는 인수가 두 곳에서 필요하기 때문입니다. 즉, 코퍼스 파일을 작성하는 test-fuzz 내부 함수와 대상 함수 본문에서 필요합니다. 이 충돌을 해결하기 위해 인수는 전자에 전달되기 전에 복제됩니다.
일반적으로 대상 함수의 인수는 serde::Serialize 및 serde::Deserialize 트레이트를 구현해야 합니다(예: deriving them을 통해). “일반적으로”라고 말하는 이유는 test-fuzz가 보통은 직렬화/역직렬화할 수 없는 특정 특수 사례를 처리하는 방법을 알고 있기 때문입니다. 예를 들어, &str 타입의 인수는 직렬화할 때 String으로 변환되고, 역직렬화할 때 다시 &str로 변환됩니다. 위의 generic_args 및 impl_generic_args도 참조하세요.
test-fuzz가 구현하는 퍼징 하니스는 전역 변수를 초기화하지 않습니다. execute_with가 어느 정도 해결책을 제공하지만, 완전한 해결책은 아닙니다. 일반적으로 전역 변수에 의존하는 함수를 퍼징하려면 임시(ad-hoc) 방법이 필요합니다.
convert 및 generic_args / impl_generic_args이 옵션들은 다음과 같은 의미에서 호환되지 않습니다. 퍼즈 타깃의 인수 타입이 타입 매개변수인 경우, convert는 매개변수가 설정된 타입이 아니라 타입 매개변수 자체를 일치시키려고 합니다. 후자를 지원하려면 컴파일러가 수행하는 방식대로 타입 치환을 시뮬레이션해야 할 것으로 보입니다. 그러나 이는 현재 구현되어 있지 않습니다.
#[cfg(test)]는 통합 테스트에서는 활성화되지 않습니다(is not enabled). 대상 함수가 통합 테스트로만 테스트된다면, enable_in_production과 TEST_FUZZ_WRITE를 사용하여 코퍼스를 생성하는 것을 고려하세요. (단, enable_in_production에 수반되는 경고에 유의하세요.)
대상 함수가 있는 패키지를 알고 있다면, cargo test/cargo test-fuzz에 -p <package>를 전달하면 빌드 시간을 크게 줄일 수 있습니다. 마찬가지로 대상 함수가 단 하나의 통합 테스트에서만 호출된다는 것을 알고 있다면, --test <name>을 전달하여 빌드 시간을 줄일 수 있습니다.
Rust는 다른 저장소의 타입에 serde::Serialize를 구현하는 것을 허용하지 않습니다(won't allow you to). 그러나 다른 저장소를 하여 해당 타입을 직렬화 가능하게 만들 수 있습니다. 또한, 의존성 저장소를 가져올 때 이 유용할 수 있습니다.
우리는 코퍼스, 크래시, 행(hang) 및 작업 큐의 형식을 변경할 수 있으며, 이러한 변경을 호환성을 깨지 않는(non-breaking) 변경으로 간주할 권리를 보유합니다.
test-fuzz는 Macros and Inline Functions Exception이 적용된 AGPLv3 라이선스에 따라 사용이 허가되고 배포됩니다. 쉽게 말하면, 소프트웨어에서 test_fuzz macro, test_fuzz_impl macro 또는 test-fuzz의 convenience functions and macros를 사용한다고 해서 해당 소프트웨어가 AGPLv3 라이선스의 적용을 받아야 하는 것은 아닙니다.
| 트레이트 | 값 |
|---|
Bounded | T::min_value(), T::max_value() |
Bounded + Add + One | T::min_value() + T::one() |
Bounded + Add + Div + Two | T::min_value() / T::two() + T::max_value() / T::two() |
Bounded + Add + Div + Two + One | T::min_value() / T::two() + T::max_value() / T::two() + T::one() |
Bounded + Sub + One | T::max_value() - T::one() |
Default | T::default() |
Two - test_fuzz::runtime::traits::Two (사실상 Add + One과 동일)Serde attributes는 구현하기 까다로운 타입에 대해 serde::Serialize/serde::Deserialize를 구현할 때 유용할 수 있습니다.