-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
39 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# list file to not track by the local-history extension. comment line starts with a '#' character | ||
# each line describe a regular expression pattern (search for "Javascript regex") | ||
# it will relate to the workspace directory root. for example: | ||
# ".*\.txt" ignores any file with "txt" extension | ||
# "/test/.*" ignores all the files under the "test" directory | ||
# ".*/test/.*" ignores all the files under any "test" directory (even under sub-folders) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"sourceFile": "src/ch10-01-syntax.md", | ||
"activeCommit": 0, | ||
"commits": [ | ||
{ | ||
"activePatchIndex": 2, | ||
"patches": [ | ||
{ | ||
"date": 1632539998178, | ||
"content": "Index: \n===================================================================\n--- \n+++ \n" | ||
}, | ||
{ | ||
"date": 1632540025654, | ||
"content": "Index: \n===================================================================\n--- \n+++ \n@@ -234,9 +234,9 @@\n operations that are available only for floating point types.\n \n Generic type parameters in a struct definition aren’t always the same as those\n you use in that struct’s method signatures. \n-Listing 10-11 uses the generic types `X1` `Y1` for the `Point` struct with\n+Listing 10-11 uses the generic types `X1` `Y1` for the `Point` struct and \n `X2` `Y2` for the `mixup` method signature to make the example clearer. \n The method creates a new `Point` instance with the `x` value from the `self` \n `Point` (of type `X1`) and the `y` value from the passed-in `Point` (of type `Y2`).\n \n" | ||
}, | ||
{ | ||
"date": 1632540105308, | ||
"content": "Index: \n===================================================================\n--- \n+++ \n@@ -258,10 +258,10 @@\n call will print `p3.x = 5, p3.y = c`.\n \n The purpose of this example is to demonstrate a situation in which some generic\n parameters are declared with `impl` and some are declared with the method\n-definition. Here, the generic parameters `T` and `U` are declared after `impl`,\n-because they go with the struct definition. The generic parameters `V` and `W`\n+definition. Here, the generic parameters `X1` and `Y1` are declared after `impl`,\n+because they go with the struct definition. The generic parameters `X2` and `Y2`\n are declared after `fn mixup`, because they’re only relevant to the method.\n \n ### Performance of Code Using Generics\n \n" | ||
} | ||
], | ||
"date": 1632539998178, | ||
"name": "Commit-0", | ||
"content": "## Generic Data Types\n\nWe can use generics to create definitions for items like function signatures or\nstructs, which we can then use with many different concrete data types. Let’s\nfirst look at how to define functions, structs, enums, and methods using\ngenerics. Then we’ll discuss how generics affect code performance.\n\n### In Function Definitions\n\nWhen defining a function that uses generics, we place the generics in the\nsignature of the function where we would usually specify the data types of the\nparameters and return value. Doing so makes our code more flexible and provides\nmore functionality to callers of our function while preventing code duplication.\n\nContinuing with our `largest` function, Listing 10-4 shows two functions that\nboth find the largest value in a slice.\n\n<span class=\"filename\">Filename: src/main.rs</span>\n\n```rust\n{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-04/src/main.rs:here}}\n```\n\n<span class=\"caption\">Listing 10-4: Two functions that differ only in their\nnames and the types in their signatures</span>\n\nThe `largest_i32` function is the one we extracted in Listing 10-3 that finds\nthe largest `i32` in a slice. The `largest_char` function finds the largest\n`char` in a slice. The function bodies have the same code, so let’s eliminate\nthe duplication by introducing a generic type parameter in a single function.\n\nTo parameterize the types in the new function we’ll define, we need to name the\ntype parameter, just as we do for the value parameters to a function. You can\nuse any identifier as a type parameter name. But we’ll use `T` because, by\nconvention, parameter names in Rust are short, often just a letter, and Rust’s\ntype-naming convention is CamelCase. Short for “type,” `T` is the default\nchoice of most Rust programmers.\n\nWhen we use a parameter in the body of the function, we have to declare the\nparameter name in the signature so the compiler knows what that name means.\nSimilarly, when we use a type parameter name in a function signature, we have\nto declare the type parameter name before we use it. To define the generic\n`largest` function, place type name declarations inside angle brackets, `<>`,\nbetween the name of the function and the parameter list, like this:\n\n```rust,ignore\nfn largest<T>(list: &[T]) -> T {\n```\n\nWe read this definition as: the function `largest` is generic over some type\n`T`. This function has one parameter named `list`, which is a slice of values\nof type `T`. The `largest` function will return a value of the\nsame type `T`.\n\nListing 10-5 shows the combined `largest` function definition using the generic\ndata type in its signature. The listing also shows how we can call the function\nwith either a slice of `i32` values or `char` values. Note that this code won’t\ncompile yet, but we’ll fix it later in this chapter.\n\n<span class=\"filename\">Filename: src/main.rs</span>\n\n```rust,ignore,does_not_compile\n{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-05/src/main.rs}}\n```\n\n<span class=\"caption\">Listing 10-5: A definition of the `largest` function that\nuses generic type parameters but doesn’t compile yet</span>\n\nIf we compile this code right now, we’ll get this error:\n\n```console\n{{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-05/output.txt}}\n```\n\nThe note mentions `std::cmp::PartialOrd`, which is a *trait*. We’ll talk about\ntraits in the next section. For now, this error states that the body of\n`largest` won’t work for all possible types that `T` could be. Because we want\nto compare values of type `T` in the body, we can only use types whose values\ncan be ordered. To enable comparisons, the standard library has the\n`std::cmp::PartialOrd` trait that you can implement on types (see Appendix C\nfor more on this trait). You’ll learn how to specify that a generic type has a\nparticular trait in the [“Traits as Parameters”][traits-as-parameters]<!--\nignore --> section, but let’s first explore other ways of using generic type\nparameters.\n\n### In Struct Definitions\n\nWe can also define structs to use a generic type parameter in one or more\nfields using the `<>` syntax. Listing 10-6 shows how to define a `Point<T>`\nstruct to hold `x` and `y` coordinate values of any type.\n\n<span class=\"filename\">Filename: src/main.rs</span>\n\n```rust\n{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-06/src/main.rs}}\n```\n\n<span class=\"caption\">Listing 10-6: A `Point<T>` struct that holds `x` and `y`\nvalues of type `T`</span>\n\nThe syntax for using generics in struct definitions is similar to that used in\nfunction definitions. First, we declare the name of the type parameter inside\nangle brackets just after the name of the struct. Then we can use the generic\ntype in the struct definition where we would otherwise specify concrete data\ntypes.\n\nNote that because we’ve used only one generic type to define `Point<T>`, this\ndefinition says that the `Point<T>` struct is generic over some type `T`, and\nthe fields `x` and `y` are *both* that same type, whatever that type may be. If\nwe create an instance of a `Point<T>` that has values of different types, as in\nListing 10-7, our code won’t compile.\n\n<span class=\"filename\">Filename: src/main.rs</span>\n\n```rust,ignore,does_not_compile\n{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-07/src/main.rs}}\n```\n\n<span class=\"caption\">Listing 10-7: The fields `x` and `y` must be the same\ntype because both have the same generic data type `T`.</span>\n\nIn this example, when we assign the integer value 5 to `x`, we let the\ncompiler know that the generic type `T` will be an integer for this instance of\n`Point<T>`. Then when we specify 4.0 for `y`, which we’ve defined to have the\nsame type as `x`, we’ll get a type mismatch error like this:\n\n```console\n{{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-07/output.txt}}\n```\n\nTo define a `Point` struct where `x` and `y` are both generics but could have\ndifferent types, we can use multiple generic type parameters. For example, in\nListing 10-8, we can change the definition of `Point` to be generic over types\n`T` and `U` where `x` is of type `T` and `y` is of type `U`.\n\n<span class=\"filename\">Filename: src/main.rs</span>\n\n```rust\n{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-08/src/main.rs}}\n```\n\n<span class=\"caption\">Listing 10-8: A `Point<T, U>` generic over two types so\nthat `x` and `y` can be values of different types</span>\n\nNow all the instances of `Point` shown are allowed! You can use as many generic\ntype parameters in a definition as you want, but using more than a few makes\nyour code hard to read. When you need lots of generic types in your code, it\ncould indicate that your code needs restructuring into smaller pieces.\n\n### In Enum Definitions\n\nAs we did with structs, we can define enums to hold generic data types in their\nvariants. Let’s take another look at the `Option<T>` enum that the standard\nlibrary provides, which we used in Chapter 6:\n\n```rust\nenum Option<T> {\n Some(T),\n None,\n}\n```\n\nThis definition should now make more sense to you. As you can see, `Option<T>`\nis an enum that is generic over type `T` and has two variants: `Some`, which\nholds one value of type `T`, and a `None` variant that doesn’t hold any value.\nBy using the `Option<T>` enum, we can express the abstract concept of having an\noptional value, and because `Option<T>` is generic, we can use this abstraction\nno matter what the type of the optional value is.\n\nEnums can use multiple generic types as well. The definition of the `Result`\nenum that we used in Chapter 9 is one example:\n\n```rust\nenum Result<T, E> {\n Ok(T),\n Err(E),\n}\n```\n\nThe `Result` enum is generic over two types, `T` and `E`, and has two variants:\n`Ok`, which holds a value of type `T`, and `Err`, which holds a value of type\n`E`. This definition makes it convenient to use the `Result` enum anywhere we\nhave an operation that might succeed (return a value of some type `T`) or fail\n(return an error of some type `E`). In fact, this is what we used to open a\nfile in Listing 9-3, where `T` was filled in with the type `std::fs::File` when\nthe file was opened successfully and `E` was filled in with the type\n`std::io::Error` when there were problems opening the file.\n\nWhen you recognize situations in your code with multiple struct or enum\ndefinitions that differ only in the types of the values they hold, you can\navoid duplication by using generic types instead.\n\n### In Method Definitions\n\nWe can implement methods on structs and enums (as we did in Chapter 5) and use\ngeneric types in their definitions, too. Listing 10-9 shows the `Point<T>`\nstruct we defined in Listing 10-6 with a method named `x` implemented on it.\n\n<span class=\"filename\">Filename: src/main.rs</span>\n\n```rust\n{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-09/src/main.rs}}\n```\n\n<span class=\"caption\">Listing 10-9: Implementing a method named `x` on the\n`Point<T>` struct that will return a reference to the `x` field of type\n`T`</span>\n\nHere, we’ve defined a method named `x` on `Point<T>` that returns a reference\nto the data in the field `x`.\n\nNote that we have to declare `T` just after `impl` so we can use it to specify\nthat we’re implementing methods on the type `Point<T>`. By declaring `T` as a\ngeneric type after `impl`, Rust can identify that the type in the angle\nbrackets in `Point` is a generic type rather than a concrete type.\n\nWe could, for example, implement methods only on `Point<f32>` instances rather\nthan on `Point<T>` instances with any generic type. In Listing 10-10 we use the\nconcrete type `f32`, meaning we don’t declare any types after `impl`.\n\n<span class=\"filename\">Filename: src/main.rs</span>\n\n```rust\n{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-10/src/main.rs:here}}\n```\n\n<span class=\"caption\">Listing 10-10: An `impl` block that only applies to a\nstruct with a particular concrete type for the generic type parameter `T`</span>\n\nThis code means the type `Point<f32>` will have a method named\n`distance_from_origin` and other instances of `Point<T>` where `T` is not of\ntype `f32` will not have this method defined. The method measures how far our\npoint is from the point at coordinates (0.0, 0.0) and uses mathematical\noperations that are available only for floating point types.\n\nGeneric type parameters in a struct definition aren’t always the same as those\nyou use in that struct’s method signatures. \nListing 10-11 uses the generic types `X1` `Y1` for the `Point` struct with\n`X2` `Y2` for the `mixup` method signature to make the example clearer. \nThe method creates a new `Point` instance with the `x` value from the `self` \n`Point` (of type `X1`) and the `y` value from the passed-in `Point` (of type `Y2`).\n\n<span class=\"filename\">Filename: src/main.rs</span>\n\n```rust\n{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-11/src/main.rs}}\n```\n\n<span class=\"caption\">Listing 10-11: A method that uses different generic types\nfrom its struct’s definition</span>\n\nIn `main`, we’ve defined a `Point` that has an `i32` for `x` (with value `5`)\nand an `f64` for `y` (with value `10.4`). The `p2` variable is a `Point` struct\nthat has a string slice for `x` (with value `\"Hello\"`) and a `char` for `y`\n(with value `c`). Calling `mixup` on `p1` with the argument `p2` gives us `p3`,\nwhich will have an `i32` for `x`, because `x` came from `p1`. The `p3` variable\nwill have a `char` for `y`, because `y` came from `p2`. The `println!` macro\ncall will print `p3.x = 5, p3.y = c`.\n\nThe purpose of this example is to demonstrate a situation in which some generic\nparameters are declared with `impl` and some are declared with the method\ndefinition. Here, the generic parameters `T` and `U` are declared after `impl`,\nbecause they go with the struct definition. The generic parameters `V` and `W`\nare declared after `fn mixup`, because they’re only relevant to the method.\n\n### Performance of Code Using Generics\n\nYou might be wondering whether there is a runtime cost when you’re using\ngeneric type parameters. The good news is that Rust implements generics in such\na way that your code doesn’t run any slower using generic types than it would\nwith concrete types.\n\nRust accomplishes this by performing monomorphization of the code that is using\ngenerics at compile time. *Monomorphization* is the process of turning generic\ncode into specific code by filling in the concrete types that are used when\ncompiled.\n\nIn this process, the compiler does the opposite of the steps we used to create\nthe generic function in Listing 10-5: the compiler looks at all the places\nwhere generic code is called and generates code for the concrete types the\ngeneric code is called with.\n\nLet’s look at how this works with an example that uses the standard library’s\n`Option<T>` enum:\n\n```rust\nlet integer = Some(5);\nlet float = Some(5.0);\n```\n\nWhen Rust compiles this code, it performs monomorphization. During that\nprocess, the compiler reads the values that have been used in `Option<T>`\ninstances and identifies two kinds of `Option<T>`: one is `i32` and the other\nis `f64`. As such, it expands the generic definition of `Option<T>` into\n`Option_i32` and `Option_f64`, thereby replacing the generic definition with\nthe specific ones.\n\nThe monomorphized version of the code looks like the following. The generic\n`Option<T>` is replaced with the specific definitions created by the compiler:\n\n<span class=\"filename\">Filename: src/main.rs</span>\n\n```rust\nenum Option_i32 {\n Some(i32),\n None,\n}\n\nenum Option_f64 {\n Some(f64),\n None,\n}\n\nfn main() {\n let integer = Option_i32::Some(5);\n let float = Option_f64::Some(5.0);\n}\n```\n\nBecause Rust compiles generic code into code that specifies the type in each\ninstance, we pay no runtime cost for using generics. When the code runs, it\nperforms just as it would if we had duplicated each definition by hand. The\nprocess of monomorphization makes Rust’s generics extremely efficient at\nruntime.\n\n[traits-as-parameters]: ch10-02-traits.html#traits-as-parameters\n" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters