diff --git a/chinese/articles/coding-bootcamp-handbook.md b/chinese/articles/coding-bootcamp-handbook.md index bfa58bf89..d6766fa51 100644 --- a/chinese/articles/coding-bootcamp-handbook.md +++ b/chinese/articles/coding-bootcamp-handbook.md @@ -389,7 +389,7 @@ Surprisingly, there are a few bootcamps that are for-profit but still free. And 要了解参加编码训练营的真实成本,你应该考虑机会成本。 -**例子**:你现在每个月赚$3000美元。你搬到旧金山,在那$2,000 美元只能租到一个卧室。你参加 12 周的训练营费用是\$15,00 0美元。 +**例子**:你现在每个月赚$3000美元。你搬到旧金山,在那$2,000 美元只能租到一个卧室。你参加 12 周的训练营费用是\$15,00 0美元。 这是你的真实成本,假设加上你在 6 个月的时间内找到工作并领取第一份薪水: diff --git a/chinese/articles/javascript-class-handbook.md b/chinese/articles/javascript-class-handbook.md index 7e6c52391..b2d575470 100644 --- a/chinese/articles/javascript-class-handbook.md +++ b/chinese/articles/javascript-class-handbook.md @@ -1666,7 +1666,7 @@ In this article, we discussed what a JavaScript class object is. We also used ex Thanks for reading! -### And here’s a useful React.JS resource: +### And here’s a useful React.JS resource I wrote a book about [Creating NPM Packages][113]! diff --git a/chinese/articles/learn-git-basics.md b/chinese/articles/learn-git-basics.md index 5c6bdfd5c..773968121 100644 --- a/chinese/articles/learn-git-basics.md +++ b/chinese/articles/learn-git-basics.md @@ -28,11 +28,11 @@ In this tutorial, I'll take you through the fundamentals of Git, covering everyt By the end of this guide, you'll have a solid understanding of Git's core concepts and be confident and well equipped with the skills to effectively use it in your development workflow. -## Prerequisites: +## Prerequisites All you need to bring to the table is a curious and eager-to-learn mindset. This guide is crafted with beginners in mind, so no prior knowledge of version control systems or programming is required. Whether you're a complete novice or have some experience with coding, you'll find this tutorial accessible and easy to follow. -## **Table of Contents:** +## **Table of Contents** 1. [What is Git?][3] – [Difference from other version control systems][4] @@ -67,21 +67,21 @@ Git is a distributed version control system that helps you and your team collabo ### What makes Git different from other Version Control Systems? -#### Conceptual Difference: +#### Conceptual Difference The big thing that sets Git apart from other tools is how it thinks about data. Instead of storing changes to files, Git thinks of its data as a series of snapshots of your project, means, every time you make a change and save it (commit), Git takes a snapshot of all your files at that moment. If a file hasn't changed, Git just keeps a link to the previous identical file. -#### Local Operations: +#### Local Operations With Git, most things you do don't need a connection to a server. Because you have the entire project history on your computer, operations are super fast. You can browse project history or see changes between versions without waiting for a server. -#### Data Integrity: +#### Data Integrity Git makes sure nothing gets lost or corrupted. Every file and directory is checksummed, and Git knows if anything changes. Git uses a SHA-1 hash, a unique code for each version of a file. If any changes are made to the content, even a single character, it will result in a different SHA-1 hash. -#### Append-Only Model: +#### Append-Only Model In Git, almost everything adds data to the project, making it hard to accidentally lose information. Once you commit changes, they are safely stored. Experimenting is less risky with Git. @@ -122,7 +122,7 @@ To view all configuration settings and their sources/origins: $ git config --list --show-origin ``` -#### How to Configure your identity in Git: +#### How to Configure your identity in Git Identity in Git is used for attributing commits properly. Let's set up your user name and email address. @@ -153,7 +153,7 @@ Make sure you provide the correct path to the executable file of your text edito By the way, these – `"-multiInst -notabbar -nosession -noPlugin"` – are options used to customize the behavior of Notepad++ when it is launched by Git. -#### How to Change default branch name in Git (optional): +#### How to Change default branch name in Git (optional) By default, when you initialize a new repository with `git init`, Git creates a branch named `master`. But from Git version 2.28 onwards, you have the option to set a different name for the initial branch. @@ -163,7 +163,7 @@ $ git config --global init.defaultBranch main changes the default branch name to 'main' globally -#### How to Check Configuration/settings in Git: +#### How to Check Configuration/settings in Git You can view your Git configuration using: @@ -224,7 +224,7 @@ $ git commit -m 'Initial commit' `git add` adds files to the staging area indicating that you want to include them in the next commit, and then commit the changes. The `-m` flag allows you to add a descriptive message to the commit. -### 2\. How to Clone an Existing Repository in Git: +### 2. How to Clone an Existing Repository in Git The second way to obtain a Git repository is by cloning an existing one. This is useful when you want to work on a project that already exists elsewhere (for example, a project you'd like to contribute to). @@ -256,7 +256,7 @@ Now that you have a Git repository set up, you'll often need to make changes and pic credit - https://git-scm.com/ -### 1\. How to Check the Status of Your Files in Git: +### 1. How to Check the Status of Your Files in Git When working with a Git repository, it's crucial to understand the status of your files. @@ -327,7 +327,7 @@ Here are some patterns you can use to work more effectively in Git. - ****Target individual files or file extensions precisely:**** For example, `test.txt` ignores only that specific file, while `*.log` ignores all files ending with `.log`. - ****Wildcards for broader matches:**** The asterisk (`*`) wildcard matches any number of characters. For example, `*.doc` ignores all files with the `.doc` extension, regardless of their name. -### 5\. How to View Changes in Git: +### 5. How to View Changes in Git If you want to see the exact changes you've made to your files before committing, you can use the `git diff` command. @@ -345,7 +345,7 @@ $ git diff --cached README.md `git diff` provides a detailed view of the actual modifications. Use `git diff ` to focus on changes within a particular file. -### 6\. How to Commit Changes: +### 6. How to Commit Changes When you are ready to commit your changes, use the `git commit` command. This opens your text editor for you to provide a commit message. Alternatively, you can use the `-m` flag to add a commit message directly: @@ -355,7 +355,7 @@ Once you have staged the changes you want to include in the commit, you can comm $ git commit -m "Your commit message here" ``` -### 7\. How to Remove Files in Git: +### 7. How to Remove Files in Git If you need to remove a file from Git's tracking, you can use `git rm`. It remove the file from both the repository and working directory. Suppose you want to remove a file named `temp.txt`: @@ -369,7 +369,7 @@ If you only want to remove it from the repository but keep it in the working dir $ git rm --cached temp.txt ``` -### 8\. How to Move (or Rename) Files in Git: +### 8. How to Move (or Rename) Files in Git Git doesn't explicitly track file movements. But you can use `git mv` to rename or move files within your repository. For example, to rename `old_file.txt` to `new_file.txt`: @@ -390,7 +390,7 @@ After creating multiple commits or cloning a repository, the `git log` command a By default, it lists commits in reverse chronological order, displaying each commit with its SHA-1 checksum, author's name and email, date, and commit message. Now let's see how can we enhance this output: -### How to View Commit Differences in Git: +### How to View Commit Differences in Git To view the difference introduced in each commit, you can use the `-p` or `--patch` option: @@ -398,7 +398,7 @@ To view the difference introduced in each commit, you can use the `-p` or `--pat $ git log -p -2 # -2 is used to view the differences introduced in each of the last two commits ``` -### How to Display Statistics in Git: +### How to Display Statistics in Git The `--stat` option provides summarized statistics for each commit, including the modified files, lines added/deleted, and a summary. @@ -406,7 +406,7 @@ The `--stat` option provides summarized statistics for each commit, including th $ git log --stat ``` -### How to Customize Git Log Output Format: +### How to Customize Git Log Output Format The `--pretty` option allows you to alter the log output format. Various options are available for different formats: @@ -440,7 +440,7 @@ Using `--graph`, you can also visualize branch and merge history. $ git log --pretty=format:"%h %s" --graph ``` -### How to Limit Git Log Output: +### How to Limit Git Log Output In addition to formatting options, `git log` offers various limiting options to refine the displayed commit history. @@ -790,7 +790,7 @@ A branch is a lightweight, movable pointer to a commit. The default branch name Creating and switching between branches allows you to work on different features simultaneously. -### How to Create a New Branch in Git: +### How to Create a New Branch in Git When you want to start working on a new feature or experiment with an idea, you can create a new branch in Git. This new branch serves as a separate line of development, allowing you to make changes without affecting the main branch. @@ -816,7 +816,7 @@ This will display a list of branches with an asterisk (\*) indicating the curren $ git branch -v ``` -### How to Switch to Another Branch in Git: +### How to Switch to Another Branch in Git To switch to an existing different branch, use `git checkout`. @@ -837,7 +837,7 @@ In Git version 2.23 onwards, you can use `git switch` instead of `git checkout`. - Switch to an existing branch: `git switch existing-branch`. - Create and switch to new branch: `git switch -c new-branch`. -### How to Visualize Branches in Git: +### How to Visualize Branches in Git After creating and switching branches, you can visualize the branch structure using: @@ -936,11 +936,11 @@ This is **different from `$ git config --global init.defaultBranch main`** that Let's understand branches in more detail and look at a common branching workflow that is used in large projects. -### Long-Running Branches: +### Long-Running Branches In Git, long-running branches are branches that remain open over an extended period. -### Topic Branches: +### Topic Branches `Topic`/`Feature` branches are short-lived branches created for specific features or pieces of work. Unlike long-running branches, which persist over time, topic branches are created, used, and often deleted once the work is complete. @@ -966,7 +966,7 @@ In Git, when you're working with branches, there are two primary ways to integra Unlike merging, which can create a cluttered history with multiple merge commits, rebasing produces a linear history, making it easier to understand the sequence of changes made over time. -### Basic Rebase Example: +### Basic Rebase Example Imagine you're working on a project with two branches: "feature" and "master". You've made some commits on the "feature" branch and now want to integrate these changes into the "master" branch using rebasing. @@ -1006,12 +1006,12 @@ Now, your project history appears linear, reflecting the sequential integration ### Rebasing vs Merging: Which is Better? -#### Rebasing Use Cases: +#### Rebasing Use Cases - Suitable for feature branches that need a clean integration into the mainline branch. - Preferred for open-source contributions where a clean commit history is valued. -#### Merging Use Cases: +#### Merging Use Cases - Appropriate for collaborative environments where transparency in the project's development process is crucial. - Useful for projects where maintaining an accurate historical record is a priority. diff --git a/chinese/articles/learning-go-from-zero-to-hero-d2a3223b3d86.md b/chinese/articles/learning-go-from-zero-to-hero-d2a3223b3d86.md deleted file mode 100644 index db099088b..000000000 --- a/chinese/articles/learning-go-from-zero-to-hero-d2a3223b3d86.md +++ /dev/null @@ -1,934 +0,0 @@ -> - 原文地址:[Learning Go—from zero to hero](https://www.freecodecamp.org/news/learning-go-from-zero-to-hero-d2a3223b3d86/) -> - 原文作者:[Milap Neupane](https://www.freecodecamp.org/news/author/milapneupane/) -> - 译者: -> - 校对者: - -![Learning Go — from zero to hero](https://cdn-media-1.freecodecamp.org/images/1*30aoNxlSnaYrLhBT0O1lzw.png) - -Let’s start with a small introduction to Go (or Golang). Go was designed by Google engineers Robert Griesemer, Rob Pike, and Ken Thompson. It is a statically typed, compiled language. The first version was released as open source in March 2012. - -> “Go is an open source programming language that makes it easy to build simple, reliable, and efficient software”. — GoLang - -In many languages, there are many ways to solve a given problem. Programmers can spend a lot of time thinking about the best way to solve it. - -Go, on the other hand, believes in fewer features — with only one right way to solve the problem. - -This saves developers time and makes the large codebase easy to maintain. There are no “expressive” features like maps and filters in Go. - -> “When you have features that add expressiveness it typically adds expense” — Rob Pike - -![1*AUiSG5Gqz8MzaGCvGpckGA](https://cdn-media-1.freecodecamp.org/images/1*AUiSG5Gqz8MzaGCvGpckGA.png) - -Recently published new logo of go lang: [https://blog.golang.org/go-brand](https://blog.golang.org/go-brand) - -### Getting Started - -Go is made of packages. The package main tells the Go compiler that the program is compiled as an executable, rather than a shared library. It is the entry point for an application. The main package is defined as: - -```go -package main -``` - -Let’s move ahead by writing a simple hello world example by creating a file `main.go` in the Go workspace. - -#### **Workspace** - -A workspace in Go is defined by the environment variable `GOPATH`. - -Any code you write is to be written inside the workspace. Go will search for any packages inside the `GOPATH` directory, or the `GOROOT` directory, which is set by default when installing Go. `GOROOT` is the path where the go is installed. - -Set `GOPATH` to your desired directory. For now, let’s add it inside a folder `~/workspace`. - -``` -# export env -export GOPATH=~/workspace -# go inside the workspace directory -cd ~/workspace -``` - -Create the file `main.go` with the following code inside the workspace folder we just created. - -#### Hello World! - -```go -package main - -import ( - "fmt" -) - -func main(){ - fmt.Println("Hello World!") -} -``` - -In the above example, `fmt` is a built-in package in Go which implements functions for formatting I/O. - -We import a package in Go by using the `import` keyword. `func main` is the main entry point where the code gets executed. `Println` is a function inside the package `fmt` which prints “hello world” for us. - -Let’s see by running this file. There are two ways we can run a Go command. As we know, Go is a compiled language, so we first need to compile it before executing. - -``` -> go build main.go -``` - -This creates a binary executable file `main` which now we can run: - -``` -> ./main -# Hello World! -``` - -There is another, simpler, way to run the program. The `go run` command helps abstract the compilation step. You can simply run the following command to execute the program. - -``` -go run main.go -# Hello World! -``` - -**_Note_**_: To try out the code that is mentioned in this blog you can use [https://play.golang.org](https://play.golang.org/)_ - -### Variables - -Variables in Go are declared explicitly. Go is a statically typed language. This means that the variable type is checked at the time of variable declaration. A variable can be declared as: - -```go -var a int -``` - -In this case, the value will be set as 0. Use the following syntax to declare and initialize a variable with a different value: - -```go -var a = 1 -``` - -Here the variable is automatically assigned as an int. We can use a shorthand definition for the variable declaration as: - -```go -message := "hello world" -``` - -We can also declare multiple variables in the same line: - -```go -var b, c int = 2, 3 -``` - -### Data types - -Like any other programming language, Go supports various different data structures. Let’s explore some of them: - -#### **Number, String, and Boolean** - -Some of the supported number store types are int, int8, int16, int32, int64, -uint, uint8, uint16, uint32, uint64, uintptr…… - -The string type stores a sequence of bytes. It is represented and declared with keyword `string`. - -A boolean value is stored using the keyword `bool`. - -Go also supports complex number type data types, which can be declared with `complex64` and `complex128`. - -```go -var a bool = true -var b int = 1 -var c string = 'hello world' -var d float32 = 1.222 -var x complex128 = cmplx.Sqrt(-5 + 12i) -``` - -#### **Arrays, Slices, and Maps** - -An array is a sequence of elements of the same data type. Arrays have a fixed length defined at declaration, so it cannot be expanded more than that. An array is declared as: - -```go -var a [5]int -``` - -Arrays can also be multidimensional. We can simply create them with the following format: - -```go -var multiD [2][3]int -``` - -Arrays are limiting for cases when the values of array changes in runtime. Arrays also do not provide the ability to get a subarray. For this, Go has a data type called slices. - -Slices store a sequence of elements and can be expanded at any time. Slice declaration is similar to array declaration — without the capacity defined: - -```go -var b []int -``` - -This creates a slice with zero capacity and zero length. Slices can also be defined with capacity and length. We can use the following syntax for it: - -```go -numbers := make([]int,5,10) -``` - -Here, the slice has an initial length of 5 and has a capacity of 10. - -Slices are an abstraction to an array. Slices use an array as an underlying structure. A slice contains three components: capacity, length, and a pointer to the underlying array as shown in the diagram below: - -![1*P0lNCO0sQwIYHLEX_mfSOQ](https://cdn-media-1.freecodecamp.org/images/1*P0lNCO0sQwIYHLEX_mfSOQ.png) - -image src: [https://blog.golang.org/go-slices-usage-and-internals](https://blog.golang.org/go-slices-usage-and-internals) - -The capacity of a slice can be increased by using the append or a copy function. An append function adds value to the end of the array and also increases the capacity if needed. - -```go -numbers = append(numbers, 1, 2, 3, 4) -``` - -Another way to increase the capacity of a slice is to use the copy function. Simply create another slice with a larger capacity and copy the original slice to the newly created slice: - -```go -// create a new slice -number2 := make([]int, 15) -// copy the original slice to new slice -copy(number2, number) -``` - -We can create a sub-slice of a slice. This can be done simply using the following command: - -```go -// initialize a slice with 4 len and values -number2 = []int{1,2,3,4} -fmt.Println(numbers) // -> [1 2 3 4] -// create sub slices -slice1 := number2[2:] -fmt.Println(slice1) // -> [3 4] -slice2 := number2[:3] -fmt.Println(slice2) // -> [1 2 3] -slice3 := number2[1:4] -fmt.Println(slice3) // -> [2 3 4] -``` - -Maps are a data type in Go, which maps keys to values. We can define a map using the following command: - -```go -var m map[string]int -``` - -Here `m` is the new map variable, which has its keys as `string` and values are `integers`. We can add keys and values easily to a map: - -```go -// adding key/value -m['clearity'] = 2 -m['simplicity'] = 3 -// printing the values -fmt.Println(m['clearity']) // -> 2 -fmt.Println(m['simplicity']) // -> 3 -``` - -### **Typecasting** - -One type of data type can be converted into another using type casting. Let’s see a simple type conversion: - -```go -a := 1.1 -b := int(a) -fmt.Println(b) -//-> 1 -``` - -Not all types of data type can be converted to another type. Make sure that the data type is compatible with the conversion. - -### Conditional Statements - -#### if else - -For conditional statements, we can use if-else statements as shown in the example below. Make sure that the curly braces are in the same line as the condition is. - -```go -if num := 9; num < 0 { - fmt.Println(num, "is negative") -} else if num < 10 { - fmt.Println(num, "has 1 digit") -} else { - fmt.Println(num, "has multiple digits") -} -``` - -#### switch case - -Switch cases helps organize multiple condition statements. The following example shows a simple switch case statement: - -```go -i := 2 -switch i { -case 1: - fmt.Println("one") -case 2: - fmt.Println("two") -default: - fmt.Println("none") -} -``` - -### Looping - -Go has a single keyword for the loop. A sngle for loop command help achieve different kinds of loops: - -```go -i := 0 -sum := 0 -for i < 10 { - sum += 1 - i++ -} -fmt.Println(sum) -``` - -The above example is similar to a while loop in C. The same for statement can be used for a normal for loop: - -```go -sum := 0 -for i := 0; i < 10; i++ { - sum += i -} -fmt.Println(sum) -``` - -Infinite loop in Go: - -```go -for { -} -``` - -### Pointers - -Go provides pointers. Pointers are the place to hold the address of a value. A pointer is defined by \*. A pointer is defined according to the type of data. Example: - -```go -var ap *int -``` - -Here `ap` is the pointer to an integer type. The `&` operator can be used to get the address of a variable. - -```go -a := 12 -ap = &a -``` - -The value pointed by the pointer can be accessed using the `*` operator: - -```go -fmt.Println(*ap) -// => 12 -``` - -Pointers are usually preferred while passing a struct as an argument or while declaring a method for a defined type. - -1. While passing value the value is actually copied which means more memory -2. With the pointer passed, the value changed by the function is reflected back in the method/function caller. - -Example: - -```go -func increment(i *int) { - *i++ -} -func main() { - i := 10 - increment(&i) - fmt.Println(i) -} -//=> 11 -``` - -Note: While you are trying out the example code in the blog, do not forget to include it with package main and import fmt or other packages when needed as shown in the first main.go example above. - -### Functions - -The main function defined in the main package is the entry point for a go program to execute. More functions can be defined and used. Let’s look into a simple example: - -```go -func add(a int, b int) int { - c := a + b - return c -} -func main() { - fmt.Println(add(2, 1)) -} -//=> 3 -``` - -As we can see in the above example, a Go function is defined using the **func** keyword followed by the function name. The **arguments** a function takes needs to be defined according to its data type, and finally the data type of the return. - -The return of a function can be predefined in function as well: - -```go -func add(a int, b int) (c int) { - c = a + b - return -} -func main() { - fmt.Println(add(2, 1)) -} -//=> 3 -``` - -Here c is defined as the return variable. So the variable c defined would be automatically returned without needing to be defined at the return statement at the end. - -You can also return multiple return values from a single function separating return values with a comma. - -```go -func add(a int, b int) (int, string) { - c := a + b - return c, "successfully added" -} -func main() { - sum, message := add(2, 1) - fmt.Println(message) - fmt.Println(sum) -} -``` - -### Method, Structs, and Interfaces - -Go is not a completely object-oriented language, but with structs, interfaces, and methods it has a lot of object-oriented support and feel. - -#### Struct - -A struct is a typed, collection of different fields. A struct is used to group data together. For example, if we want to group data of a Person type, we define a person’s attribute which could include name, age, gender. A struct can be defined using the following syntax: - -```go -type person struct { - name string - age int - gender string -} -``` - -With a person type struct defined, now let’s create a person: - -```go -//way 1: specifying attribute and value -p = person{name: "Bob", age: 42, gender: "Male"} -//way 2: specifying only value -person{"Bob", 42, "Male"} -``` - -We can easily access these data with a dot(.) - -```go -p.name -//=> Bob -p.age -//=> 42 -p.gender -//=> Male -``` - -You can also access attributes of a struct directly with its pointer: - -```go -pp = &person{name: "Bob", age: 42, gender: "Male"} -pp.name -//=> Bob -``` - -#### Methods - -Methods are a special type of function with a _receiver._ A receiver can be both a value or a pointer. Let’s create a method called describe which has a receiver type person we created in the above example: - -```go -package main -import "fmt" - -// struct defination -type person struct { - name string - age int - gender string -} - -// method defination -func (p *person) describe() { - fmt.Printf("%v is %v years old.", p.name, p.age) -} -func (p *person) setAge(age int) { - p.age = age -} - -func (p person) setName(name string) { - p.name = name -} - -func main() { - pp := &person{name: "Bob", age: 42, gender: "Male"} - pp.describe() - // => Bob is 42 years old - pp.setAge(45) - fmt.Println(pp.age) - //=> 45 - pp.setName("Hari") - fmt.Println(pp.name) - //=> Bob -} -``` - -As we can see in the above example, the method now can be called using a dot operator as `pp.describe`. Note that the receiver is a pointer. With the pointer we are passing a reference to the value, so if we make any changes in the method it will be reflected in the receiver pp. It also does not create a new copy of the object, which saves memory. - -Note that in the above example the value of age is changed, whereas the value of name is not changed because the method setName is of the receiver type whereas setAge is of type pointer. - -#### Interfaces - -Go interfaces are a collection of methods. Interfaces help group together the properties of a type. Let’s take the example of an interface animal: - -```go -type animal interface { - description() string -} -``` - -Here animal is an interface type. Now let’s create 2 different type of animals which implement the animal interface type: - -```go -package main - -import ( - "fmt" -) - -type animal interface { - description() string -} - -type cat struct { - Type string - Sound string -} - -type snake struct { - Type string - Poisonous bool -} - -func (s snake) description() string { - return fmt.Sprintf("Poisonous: %v", s.Poisonous) -} - -func (c cat) description() string { - return fmt.Sprintf("Sound: %v", c.Sound) -} - -func main() { - var a animal - a = snake{Poisonous: true} - fmt.Println(a.description()) - a = cat{Sound: "Meow!!!"} - fmt.Println(a.description()) -} - -//=> Poisonous: true -//=> Sound: Meow!!! -``` - -In the main function, we create a variable `a` of type animal. We assign a snake and a cat type to the animal and use Println to print a.description. Since we have implemented the method describe in both of the types (cat and snake) in a different way we get the description of the animal printed. - -### Packages - -We write all code in Go in a package. The **main** package is the entry point for the program execution. There are lots of built-in packages in Go. The most famous one we have been using is the **fmt** package. - -> “Go packages in the main mechanism for programming in the large that go provides and they make possible to divvy up a large project into smaller pieces.” - -> — Robert Griesemer - -#### Installing a package - -``` -go get -// example -go get github.com/satori/go.uuid -``` - -The packages we installed are saved inside the GOPATH env which is our work directory. You can see the packages by going inside the pkg folder inside our work directory `cd $GOPATH/pkg`. - -#### Creating a custom package - -Let’s start by creating a folder custom\_package: - -``` -> mkdir custom_package -> cd custom_package -``` - -To create a custom package we need to first create a folder with the package name we need. Let’s say we are building a package `person`. For that let’s create a folder named `person` inside `custom_package` folder: - -``` -> mkdir person -> cd person -``` - -Now let’s create a file person.go inside this folder. - -```go -package person -func Description(name string) string { - return "The person name is: " + name -} -func secretName(name string) string { - return "Do not share" -} -``` - -We now need to install the package so that it can be imported and used. So let’s install it: - -``` -> go install -``` - -Now let’s go back to the custom\_package folder and create a main.go file - -```go -package main -import( - "custom_package/person" - "fmt" -) -func main(){ - p := person.Description("Milap") - fmt.Println(p) -} -// => The person name is: Milap -``` - -Here we can now import the package `person` we created and use the function Description. Note that the function `secretName` we created in the package will not be accessible. In Go, the method name starting without a capital letter will be private. - -#### **Packages Documentation** - -Go has built-in support for documentation for packages. Run the following command to generate documentation: - -``` -godoc person Description -``` - -This will generate documentation for the Description function inside our package person. To see the documentation run a web server using the following command: - -``` -godoc -http=":8080" -``` - -Now go to the URL [http://localhost:8080/pkg/](http://localhost:6060/pkg/) and see the documentation of the package we just created. - -#### Some built-in packages in Go - -**fmt** - -The package implements formatted I/O functions. We have already used the package for printing out to stdout. - -**json** - -Another useful package in Go is the json package. This helps to encode/decode the JSON. Let’s take an example to encode/decode some json: - -Encode - -```go -package main - -import ( - "fmt" - "encoding/json" -) - -func main(){ - mapA := map[string]int{"apple": 5, "lettuce": 7} - mapB, _ := json.Marshal(mapA) - fmt.Println(string(mapB)) -} -``` - -Decode - -```go -package main - -import ( - "fmt" - "encoding/json" -) - -type response struct { - PageNumber int `json:"page"` - Fruits []string `json:"fruits"` -} - -func main(){ - str := `{"page": 1, "fruits": ["apple", "peach"]}` - res := response{} - json.Unmarshal([]byte(str), &res) - fmt.Println(res.PageNumber) -} -//=> 1 -``` - -While decoding the json byte using unmarshal, the first argument is the json byte and the second argument is the address of the response type struct where we want the json to be mapped to. Note that the `json:”page”` maps page key to PageNumber key in the struct. - -### Error Handling - -Errors are the undesired and unexpected result of a program. Let’s say we are making an API call to an external service. This API call may be successful or could fail. An error in a Go program can be recognized when an error type is present. Let’s see the example: - -```go -resp, err := http.Get("http://example.com/") -``` - -Here the API call to the error object may pass or could fail. We can check if the error is nil or present and handle the response accordingly: - -```go -package main - -import ( - "fmt" - "net/http" -) - -func main(){ - resp, err := http.Get("http://example.com/") - if err != nil { - fmt.Println(err) - return - } - fmt.Println(resp) -} -``` - -#### Returning custom error from a function - -When we are writing a function of our own, there are cases when we have errors. These errors can be returned with the help of the error object: - -```go -func Increment(n int) (int, error) { - if n < 0 { - // return error object - return nil, errors.New("math: cannot process negative number") - } - return (n + 1), nil -} -func main() { - num := 5 - - if inc, err := Increment(num); err != nil { - fmt.Printf("Failed Number: %v, error message: %v", num, err) - }else { - fmt.Printf("Incremented Number: %v", inc) - } -} -``` - -Most of the packages that are built in Go, or external packages we use, have a mechanism for error handling. So any function we call could have possible errors. These errors should never be ignored and always handled gracefully in the place we call these functions, as we have done in the above example. - -#### Panic - -Panic is something that is unhandled and is suddenly encountered during a program execution. In Go, panic is not the ideal way to handle exceptions in a program. It is recommended to use an error object instead. When a panic occurs, the program execution get’s halted. The thing that gets executed after a panic is the defer. - -```go -//Go -package main - -import "fmt" - -func main() { - f() - fmt.Println("Returned normally from f.") -} - -func f() { - defer func() { - if r := recover(); r != nil { - fmt.Println("Recovered in f", r) - } - }() - fmt.Println("Calling g.") - g(0) - fmt.Println("Returned normally from g.") -} - -func g(i int) { - if i > 3 { - fmt.Println("Panicking!") - panic(fmt.Sprintf("%v", i)) - } - defer fmt.Println("Defer in g", i) - fmt.Println("Printing in g", i) - g(i + 1) -} -``` - -#### Defer - -Defer is something that will always get executed at the end of a function. - -In the above example, we panic the execution of the program using panic(). As you notice, there is a defer statement which will make the program execute the line at the end of the execution of the program. Defer can also be used when we need something to be executed at the end of the function, for example closing a file. - -### Concurrency - -Go is built with concurrency in mind. Concurrency in Go can be achieved by Go routines which are lightweight threads. - -**Go routine** - -Go routines are the function which can run in parallel or concurrently with another function. Creating a Go routine is very simple. Simply by adding a keyword Go in front of a function, we can make it execute in parallel. Go routines are very lightweight, so we can create thousands of them. Let’s look into a simple example: - -```go -package main -import ( - "fmt" - "time" -) -func main() { - go c() - fmt.Println("I am main") - time.Sleep(time.Second * 2) -} -func c() { - time.Sleep(time.Second * 2) - fmt.Println("I am concurrent") -} -//=> I am main -//=> I am concurrent -``` - -As you can see in the above example, the function c is a Go routine which executes in parallel with the main Go thread. There are times we want to share resources between multiple threads. Go prefers not sharing the variables of one thread with another because this adds a chance of deadlock and resource waiting. There is another way to share resources between Go routines: via go channels. - -**Channels** - -We can pass data between two Go routines using channels. While creating a channel it is necessary to specify what kind of data the channel receives. Let’s create a simple channel with string type as follows: - -```go -c := make(chan string) -``` - -With this channel, we can send string type data. We can both send and receive data in this channel: - -```go -package main - -import "fmt" - -func main(){ - c := make(chan string) - go func(){ c <- "hello" }() - msg := <-c - fmt.Println(msg) -} -//=>"hello" -``` - -The receiver Channels wait until the sender sends data to the channel. - -**One way channel** - -There are cases where we want a Go routine to receive data via the channel but not send data, and also vice versa. For this, we can also create a **one-way channel**. Let’s look into a simple example: - -```go -package main - -import ( - "fmt" -) - -func main() { - ch := make(chan string) - - go sc(ch) - fmt.Println(<-ch) -} - -func sc(ch chan<- string) { - ch <- "hello" -} -``` - -In the above example, `sc` is a Go routine which can only send messages to the channel but cannot receive messages. - -### Organizing multiple channels for a Go routine using select - -There may be multiple channels that a function is waiting on. For this, we can use a select statement. Let us take a look at an example for more clarity: - -```go -package main - -import ( - "fmt" - "time" -) - -func main() { - c1 := make(chan string) - c2 := make(chan string) - go speed1(c1) - go speed2(c2) - fmt.Println("The first to arrive is:") - select { - case s1 := <-c1: - fmt.Println(s1) - case s2 := <-c2: - fmt.Println(s2) - } -} - -func speed1(ch chan string) { - time.Sleep(2 * time.Second) - ch <- "speed 1" -} - -func speed2(ch chan string) { - time.Sleep(1 * time.Second) - ch <- "speed 2" -} -``` - -In the above example, the main is waiting on two channels, c1 and c2. With select case statement the main function prints, the message sends from the channel whichever it receives first. - -**Buffered channel** - -You can create a buffered channel in go. With a buffered channel, the messages send to the channel will be blocked if the buffer is full. Let’s take a look at the example: - -```go -package main - -import "fmt" - -func main(){ - ch := make(chan string, 2) - ch <- "hello" - ch <- "world" - ch <- "!" # extra message in buffer - fmt.Println(<-ch) -} - -# => fatal error: all goroutines are asleep - deadlock! -``` - -As we see in above no more than 2 messages are accepted by a channel. - -#### Why is Golang Successful? - -> Simplicity…… — Rob-pike - -### Great! - -We learned some of the major components and features of Go. - -1. Variables, Datatypes -2. Array slices and maps -3. Functions -4. Looping and conditional statements -5. Pointers -6. Packages -7. Method, Structs, and Interfaces -8. Error Handling -9. Concurrency — Go routines and channels - -Congratulations, you now have a decent understanding of Go. - -> One of my most productive days was throwing away 1,000 lines of code. - -> — Ken Thompson - -Do not stop here. Keep moving forward. Think about a small application and start building. - -[LinkedIn](https://www.linkedin.com/in/milap-neupane-99a4b565/), [Github](http://github.com/milap-neupane), [Twitter](https://twitter.com/_milap) - -Also Posted on Milap Neupane Blog: [Learning Go-from zero to hero](https://milapneupane.com.np/2019/07/06/learning-golang-from-zero-to-hero/) \ No newline at end of file diff --git a/chinese/articles/learning-go-from-zero-to-hero.md b/chinese/articles/learning-go-from-zero-to-hero.md new file mode 100644 index 000000000..5a46402fd --- /dev/null +++ b/chinese/articles/learning-go-from-zero-to-hero.md @@ -0,0 +1,937 @@ +> - 原文地址:[Learning Go—from zero to hero](https://www.freecodecamp.org/news/learning-go-from-zero-to-hero-d2a3223b3d86/) +> - 原文作者:[Milap Neupane](https://www.freecodecamp.org/news/author/milapneupane/) +> - 译者:[luojiyin](https://github.com/luojiyin1987) +> - 校对者: + +![Learning Go — from zero to hero](https://cdn-media-1.freecodecamp.org/images/1*30aoNxlSnaYrLhBT0O1lzw.png) + +让我们先对 Go(或称 Golang )做一个小小的介绍。Go 是由谷歌工程师 Robert Griesemer、Rob Pike 和 Ken Thompson 设计的。它是一种静态类型的、编译的语言。第一个版本于 2012 年 3 月作为开源版本发布。 + +> "Go 是一种开源的编程语言,它使人们能够轻松地构建简单、可靠和高效的软件"。- GoLang + +在许多编程语言中,有许多方法来解决一个特定的问题。程序员要花很多时间去思考解决它的最佳方法。 + +Go 却相信用较少的功能--只有一种正确的方式来解决问题。 + +这为开发人员节省了时间,并使大型代码库易于维护。 Go 中没有像 `maps` 和 `filters` 这样的 "表达性 "功能。 + +> "当你有增加表现力的功能时,通常会增加系统开销。"--Rob Pike + +![1*AUiSG5Gqz8MzaGCvGpckGA](https://cdn-media-1.freecodecamp.org/images/1*AUiSG5Gqz8MzaGCvGpckGA.png) + +最近发表的新的 golang 标志: [https://blog.golang.org/go-brand](https://blog.golang.org/go-brand) + +### 入门 + +Go 是由 packages(包) 组成的。package main 告诉 Go 编译器,该程序被编译为可执行文件,而不是共享库。它是一个应用程序的入口点。package main 的定义如下: + +```go +package main +``` + +让我们继续前进,在 Go workspace 创建一个 `main.go` 文件,编写一个简单的 hello world 例子。 + +#### **Workspace** + +Go 中的 workspace 是由环境变量 `GOPATH` 定义的。 + +你写的任何代码都要写在 workspace 里面。Go 将搜索 `GOPATH` 目录内的任何软件包,或者 `GOROOT` 目录,该目录在安装 Go 时默认设置。`GOROOT` 是安装 Go 的路径。 + +设置 `GOPATH` 到你想要的目录。现在,让我们把它添加到 `~/workspace` 文件夹内。 + +```shell +# export env +export GOPATH=~/workspace +# go inside the workspace directory +cd ~/workspace +``` + +在我们刚刚创建的 workspace 文件夹中创建 `main.go` 文件,其中包含以下代码。 + +#### Hello World + +```go +package main + +import ( + "fmt" +) + +func main(){ + fmt.Println("Hello World!") +} +``` + +在上面的例子中,`fmt`是 Go 中的一个内置包,它实现了用于格式化 I/O 输出的函数。 + +我们通过使用 `import` 关键字在 Go 中导入一个包。`func main` 是代码被执行的主入口点。`Println` 是包 `fmt` 中的一个函数,它为我们打印出 "hello world"。 + +让我们通过运行这个文件来看看。我们有两种方法可以运行 Go 命令。正如我们所知,Go 是一种编译语言,所以我们首先需要在执行之前编译它。 + +```shell +> go build main.go +``` + +这将创建一个二进制可执行文件`main`,现在我们可以运行: + +```shell +> ./main +# Hello World! +``` + +还有一种更简单的方法来运行程序。`go run` 命令会编译源代码,并直接执行源码中的 main() 函数,不会在当前目录留下可执行文件。你可以简单地运行以下命令来执行该程序。 + +```shell +go run main.go +# Hello World! +``` + +**_注意_** : _要尝试本博客中提到的代码,你可以使用 [https://play.golang.org](https://play.golang.org/)_ + +### Variables(变量) + +Go 中的变量是明确声明的。Go 是一种静态类型的语言。这意味着在声明变量的时候会检查变量的类型。一个变量: + +```go +var a int +``` + +在这种情况下,值将被设置为 0。 使用下面的语法来声明和初始化一个具有不同值的变量: + +```go +var a = 1 +``` + +这里的变量被自动分配为`int`。 我们可以对变量的声明使用一个简短定义,即: + +```go +message := "hello world" +``` + +我们也可以在同一行中声明多个变量: + +```go +var b, c int = 2, 3 +``` + +### Data types(数据类型) + +像其他编程语言一样,Go 支持各种不同的数据结构。让我们来探索其中: + +#### **Number, String, and Boolean (整型 字符串和布尔值)** + +支持的整型包括 int, int8, int16, int32, int64, +uint, uint8, uint16, uint32, uint64, uintptr(无符号整型,长度跟平台相关,它的长度可以用来保存一个指针地址) 等 + +字符串类型存储一个字节序列。它用关键字 `string` 来表示和声明。 + +布尔值使用关键字 `bool` 来存储。 + +Go 也支持复数类型,可以用 `complex64` 和 `complex128` 来声明。 + +```go +var a bool = true +var b int = 1 +var c string = 'hello world' +var d float32 = 1.222 +var x complex128 = cmplx.Sqrt(-5 + 12i) +``` + +#### **Arrays, Slices, and Maps( 数组、切片和映射)** + +数组是由相同数据类型的元素组成的一个序列。数组在声明时有一个固定的长度,所以它不能被扩大到超过这个长度。声明一个数组: + +```go +var a [5]int +``` + +数组也可以是多维的。我们可以简单地用以下方式创建它们: + +```go +var multiD [2][3]int +``` + +在运行时更改数组是受限的。数组也没有提供获取子数组的能力。 为此,Go 有一种数据类型,叫做切片(slices)。 + +切片存储了一连串的元素,并且可以在任何时候扩展。切片声明与数组声明类似--但没有定义容量: + +```go +var b []int +``` + +这将创建一个容量为 0、长度为 0 的切片。 + +也可以用容量和长度来定义切片。我们可以用下面的语法来定义它: + +```go +numbers := make([]int,5,10) +``` + +这里,切片的初始长度为 5,容量为 10。 + +切片是对数组的一种抽象。切片使用一个数组作为底层结构。一个切片包含三个部分:容量、长度和一个指向底层数组的指针,如下图所示: + +![1*P0lNCO0sQwIYHLEX_mfSOQ](https://cdn-media-1.freecodecamp.org/images/1*P0lNCO0sQwIYHLEX_mfSOQ.png) + +图片源自: [https://blog.golang.org/go-slices-usage-and-internals](https://blog.golang.org/go-slices-usage-and-internals) + +一个切片的容量可以通过使用 append 或 copy 函数来增加。append 函数将值添加到数组的末端,如果需要的话也可以增加容量。 + +```go +numbers = append(numbers, 1, 2, 3, 4) +``` + +另一种增加切片容量的方法是使用 copy 函数。简单地创建另一个容量更大的切片,并将原来的切片复制到新创建的切片上: + +```go +// create a new slice +number2 := make([]int, 15) +// copy the original slice to new slice +copy(number2, number) +``` + +我们可以创建一个切片的子切片。这可以通过以下命令简单地完成: + +```go +// initialize a slice with 4 len and values +number2 = []int{1,2,3,4} +fmt.Println(numbers) // -> [1 2 3 4] +// create sub slices +slice1 := number2[2:] +fmt.Println(slice1) // -> [3 4] +slice2 := number2[:3] +fmt.Println(slice2) // -> [1 2 3] +slice3 := number2[1:4] +fmt.Println(slice3) // -> [2 3 4] +``` + +映射是 Go 中的一种数据类型,它将键映射到值。我们可以使用以下命令来定义一个 map: + +```go +var m map[string]int +``` + +`m` 是新的 map 变量, 它的键是 `string` 类型, 值是 `integers` 类型。我们很容易在 map 上添加键值对: + +```go +// adding key/value +m['clearity'] = 2 +m['simplicity'] = 3 +// printing the values +fmt.Println(m['clearity']) // -> 2 +fmt.Println(m['simplicity']) // -> 3 +``` + +### **Typecasting (类型转换)** + +一种类型的数据类型可以通过类型转换转换为另一种类型。让我们看看一个简单的类型转换: + +```go +a := 1.1 +b := int(a) +fmt.Println(b) +//-> 1 +``` + +不是所有类型的数据类型都可以转换为另一种类型。请确保数据类型与转换的内容相匹配。 + +### Conditional Statements (条件语句) + +#### if else + +对于条件性语句,我们可以使用 if-else 语句,如下例所示。请确保大括号与条件语句在同一行。 + +```go +if num := 9; num < 0 { + fmt.Println(num, "is negative") +} else if num < 10 { + fmt.Println(num, "has 1 digit") +} else { + fmt.Println(num, "has multiple digits") +} +``` + +#### switch case + +Switch cases 有助于组织多个条件语句。下面的例子显示了一个简单的 switch 语句: + +```go +i := 2 +switch i { +case 1: + fmt.Println("one") +case 2: + fmt.Println("two") +default: + fmt.Println("none") +} +``` + +### Looping (循环) + +Go 有一个循环的关键词 `for`。for 循环命令用于实现不同种类的循环: + +```go +i := 0 +sum := 0 +for i < 10 { + sum += 1 + i++ +} +fmt.Println(sum) +``` + +上面的例子类似于 C 语言中的 while 循环。 + +Go 中的 for 语句也可以用于普通的 for 循环: + +```go +sum := 0 +for i := 0; i < 10; i++ { + sum += i +} +fmt.Println(sum) +``` + +Go 中的死循环: + +```go +for { +} +``` + +### Pointers (指针) + +Go 提供了指针。指针是用来保存一个变量的地址的地方。指针是由 \* 定义的。指针是根据数据的类型来定义的。 例如: + +```go +var ap *int +``` + +`ap` 是指向一个整数类型的指针。 `&` 操作符可以用来获取一个变量的地址。 + +```go +a := 12 +ap = &a +``` + +指针所指向的值可以使用 `*` 操作符来访问: + +```go +fmt.Println(*ap) +// => 12 +``` + +在传递结构体作为参数时,或者在为定义的类型声明方法时,通常倾向于使用指针。 + +1. 传递值时,实际上是在复制值,这意味着更多的内存。 +2. 传递指针,函数改变的值会反映在 方法/函数 调用者身上 + +例如: + +```go +func increment(i *int) { + *i++ +} +func main() { + i := 10 + increment(&i) + fmt.Println(i) +} +//=> 11 +``` + +注意:当你在尝试博客中的示例代码时,不要忘记包含 `package main`,并在需要时导入 `fmt` 或其他包,如上面第一个 main.go 例子中所示。 + +### Functions (函数) + +在 main package 中定义的 main 函数是 go 程序执行的入口。更多的函数可以被定义和使用。让我们来看看一个简单的例子。: + +```go +func add(a int, b int) int { + c := a + b + return c +} +func main() { + fmt.Println(add(2, 1)) +} +//=> 3 +``` + +在上面的例子中我们可以看到,Go 函数是用 **func** 关键字来定义的,后面是函数名称。一个函数的 **参数** 需要根据其数据类型来定义,最后是返回的数据类型。 + +一个函数的返回值也可以在函数中预先定义: + +```go +func add(a int, b int) (c int) { + c = a + b + return +} +func main() { + fmt.Println(add(2, 1)) +} +//=> 3 +``` + +这里 c 被定义为返回变量。所以定义的变量 c 会自动返回,而不需要在最后的返回语句中定义。 + +你也可以从一个函数中返回多个返回值,用逗号来分隔返回值。 + +```go +func add(a int, b int) (int, string) { + c := a + b + return c, "successfully added" +} +func main() { + sum, message := add(2, 1) + fmt.Println(message) + fmt.Println(sum) +} +``` + +### Method, Structs, and Interfaces (方法,结构体,接口) + +Go 并不是一种完全面向对象的语言,但通过结构体(Struct)、接口(Interface)和方法(Method),它有很多面向对象的支持和感觉。 + +#### Struct (结构体) + +结构体是一种类型化的、不同字段的集合。结构体用于将数据分组。例如,如果我们想对 Person 类型的数据进行分组,我们可以定义一个人的属性,其中可能包括姓名、年龄、性别。可以使用以下语法来定义一个结构体: + +```go +type person struct { + name string + age int + gender string +} +``` + +在定义了一个人的类型结构后,现在让我们来创建一个 person: + +```go +//way 1: specifying attribute and value +p = person{name: "Bob", age: 42, gender: "Male"} +//way 2: specifying only value +person{"Bob", 42, "Male"} +``` + +我们可以很容易地用一个点(.)来访问这些数据。 + +```go +p.name +//=> Bob +p.age +//=> 42 +p.gender +//=> Male +``` + +你也可以用结构的指针直接访问其属性: + +```go +pp = &person{name: "Bob", age: 42, gender: "Male"} +pp.name +//=> Bob +``` + +#### Methods (方法) + +方法(Method)是一种特殊的函数类型,它有一个 _receiver_ 。 _receiver_ 可以是一个值或一个指针。让我们创建一个名为 describe 的方法(Method),它有一个我们在上面的例子中创建的接收器类型的 person: + +```go +package main +import "fmt" + +// struct definition +type person struct { + name string + age int + gender string +} + +// method definition +func (p *person) describe() { + fmt.Printf("%v is %v years old.", p.name, p.age) +} +func (p *person) setAge(age int) { + p.age = age +} + +func (p person) setName(name string) { + p.name = name +} + +func main() { + pp := &person{name: "Bob", age: 42, gender: "Male"} + pp.describe() + // => Bob is 42 years old + pp.setAge(45) + fmt.Println(pp.age) + //=> 45 + pp.setName("Hari") + fmt.Println(pp.name) + //=> Bob +} +``` + +正如我们在上面的例子中看到的,现在可以使用点运算符来调用该方法,如 `pp.describe`。请注意,_receiver_ 是一个指针。使用指针,我们传递的是一个值的引用,所以如果我们在方法中做任何改变,都会反映在 _receiver_ pp 中。它也不会创建一个新的对象的副本,这就节省了内存。 + +请注意,在上面的例子中,年龄的值被改变了,而名字的值没有改变,因为 setName 方法是 _receiver_ 类型是值类型,而 setAge 是指针类型的。 + +#### Interfaces (接口) + +Go 接口(interfaces)是一个方法(methods)的集合。接口有助于将一个类型的属性组合在一起。让我们以一个接口 animal 为例: + +```go +type animal interface { + description() string +} +``` + +animal 是一个接口(interface)类型。现在让我们创建两个不同类型的 animal,它们都实现了 animal 接口类型: + +```go +package main + +import ( + "fmt" +) + +type animal interface { + description() string +} + +type cat struct { + Type string + Sound string +} + +type snake struct { + Type string + Poisonous bool +} + +func (s snake) description() string { + return fmt.Sprintf("Poisonous: %v", s.Poisonous) +} + +func (c cat) description() string { + return fmt.Sprintf("Sound: %v", c.Sound) +} + +func main() { + var a animal + a = snake{Poisonous: true} + fmt.Println(a.description()) + a = cat{Sound: "Meow!!!"} + fmt.Println(a.description()) +} + +//=> Poisonous: true +//=> Sound: Meow!!! +``` + +type cat struct { +在主函数中,我们创建一个动物类型的变量 `a`。我们给动物分配一个 snake 和一个 cat 的类型,并使用 Println 来打印 a.description。由于我们在两种类型(cat 和 snake)中都以不同的方式实现了 describe 方法,我们得到了打印的动物描述。 + +### Packages (包) + +我们把 Go 的所有代码都写在一个包里。**main** package 是程序执行的入口点。Go 中有很多内置包。我们一直在使用的最著名的是**fmt**包。 + +> "Go 软件包是 Go 提供的大型编程的主要机制,它们使得将一个大型项目分割成小块成为可能。" +> — Robert Griesemer + +#### Installing a package (安装一个包) + +```shell +go get +// example +go get github.com/satori/go.uuid +``` + +我们安装的软件包被保存在 GOPATH 环境变量设置的工作目录。你可以通过进入我们工作目录下的 pkg 文件夹 `cd $GOPATH/pkg` 来查看这些软件包。 + +#### Creating a custom package (创建自定义包) + +让我们先创建一个文件夹 custom_package: + +```shell +> mkdir custom_package +> cd custom_package +``` + +要创建一个自定义包,我们需要首先创建一个文件夹,并加上我们需要的包名。比方说,我们要建立一个 `person` 包。为此,让我们在 `custom_package` 文件夹中创建一个名为 `person` 的文件夹。: + +```shell +> mkdir person +> cd person +``` + +现在让我们在这个文件夹中创建一个文件 person.go。 + +```go +package person +func Description(name string) string { + return "The person name is: " + name +} +func secretName(name string) string { + return "Do not share" +} +``` + +我们现在需要安装这个包,以便它可以被导入和使用。因此,让我们来安装它: + +```shell +> go install +``` + +现在让我们回到 custom_package 文件夹,创建一个 main.go 文件 + +```go +package main +import( + "custom_package/person" + "fmt" +) +func main(){ + p := person.Description("Milap") + fmt.Println(p) +} +// => The person name is: Milap +``` + +在这里,我们现在可以导入我们创建的包 `person` 并使用函数 Description。注意,我们在包中创建的函数 `secretName` 将不能被访问。在 Go 中,没有大写字母开头的方法名称将是私有的。 + +#### **Packages Documentation (包文档)** + +Go 内置了对包的文档支持。运行以下命令来生成文档: + +```shell +godoc person Description +``` + +这将为我们的包 person 里面的描述函数生成文档。要看到这些文档,请使用以下命令运行一个网络服务器: + +```shell +godoc -http=":8080" +``` + +现在去 URL [http://localhost:8080/pkg/](http://localhost:6060/pkg/),看看我们刚刚创建的包的文档。 + +#### Some built-in packages in Go (Go 内置包) + +**fmt** + +该包实现了格式化的 I/O 函数。我们已经用这个包实现了向 stdout 打印的功能。 + +**json** + +Go 中另一个有用的包是 json 包。这有助于对 JSON 进行编码/解码。让我们举个例子,对一些 JSON 进行编码/解码: + +编码 + +```go +package main + +import ( + "fmt" + "encoding/json" +) + +func main(){ + mapA := map[string]int{"apple": 5, "lettuce": 7} + mapB, _ := json.Marshal(mapA) + fmt.Println(string(mapB)) +} +``` + +解码 + +```go +package main + +import ( + "fmt" + "encoding/json" +) + +type response struct { + PageNumber int `json:"page"` + Fruits []string `json:"fruits"` +} + +func main(){ + str := `{"page": 1, "fruits": ["apple", "peach"]}` + res := response{} + json.Unmarshal([]byte(str), &res) + fmt.Println(res.PageNumber) +} +//=> 1 +``` + +当使用 unmarshal 解码 json 字节时,第一个参数是 json 字节,第二个参数是我们希望 json 被映射到的响应类型结构的地址。注意,`json: "page"`将页面键映射到结构中的 PageNumber 键。 + +### Error Handling (错误处理) + +错误是指程序中不想要的和意外的结果。比方说,我们正在对一个外部服务进行 API 调用。这个 API 调用可能是成功的,也可能是失败的。当错误类型出现时,Go 程序中的错误可以被识别。让我们看看这个例子: + +```go +resp, err := http.Get("http://example.com/") +``` + +在这里,API 调用可能通过也可能失败。我们可以检查错误是否为 `nil`或存在,并相应地处理响应: + +```go +package main + +import ( + "fmt" + "net/http" +) + +func main(){ + resp, err := http.Get("http://example.com/") + if err != nil { + fmt.Println(err) + return + } + fmt.Println(resp) +} +``` + +#### Returning custom error from a function (从函数返回自定义错误) + +当我们在编写自己的函数时,有些情况下会出现错误。这些错误可以在错误对象的帮助下返回: + +```go +func Increment(n int) (int, error) { + if n < 0 { + // return error object + return nil, errors.New("math: cannot process negative number") + } + return (n + 1), nil +} +func main() { + num := 5 + + if inc, err := Increment(num); err != nil { + fmt.Printf("Failed Number: %v, error message: %v", num, err) + }else { + fmt.Printf("Incremented Number: %v", inc) + } +} +``` + +大多数 Go 中内置的包,或者我们使用的外部包,都有一个错误处理的机制。所以我们调用的任何函数都有可能出现错误。这些错误绝不应该被忽视,总是在我们调用这些函数的地方优雅地处理,正如我们在上面的例子中所做的那样。 + +#### Panic + +Panic 是指在程序执行过程中突然遇到的未被处理的东西。在 Go 中,Panic 不是处理程序中异常的理想方式。建议使用一个错误对象来代替。当 Panic 发生时,程序的执行会被停止。Panic 发生后被执行的东西是 defer。 + +```go +//Go +package main + +import "fmt" + +func main() { + f() + fmt.Println("Returned normally from f.") +} + +func f() { + defer func() { + if r := recover(); r != nil { + fmt.Println("Recovered in f", r) + } + }() + fmt.Println("Calling g.") + g(0) + fmt.Println("Returned normally from g.") +} + +func g(i int) { + if i > 3 { + fmt.Println("Panicking!") + panic(fmt.Sprintf("%v", i)) + } + defer fmt.Println("Defer in g", i) + fmt.Println("Printing in g", i) + g(i + 1) +} +``` + +#### Defer + +Defer 是指总是在函数的末尾被执行的东西。 + +在上面的例子中,我们用 panic() 使程序的执行陷入 panic。正如你所注意到的,这里有一个 defer 语句,它将使程序在最后执行这一行。当我们需要在函数结束时执行一些东西时也可以使用 defer,例如关闭一个文件。 + +### Concurrency (并发) + +Go 是在考虑到并发性的情况下建立的。Go 中的并发性可以通过 Go 协程实现,它是轻量级的线程。 + +**Go routine (Go 协程)** + +Go 协程是可以与另一个函数并行或同时运行的函数。创建一个 Go 协程非常简单。只需在一个函数前面加上关键字 Go,我们就可以让它并行执行。Go 协程是非常轻量级的,所以我们可以创建成千上万的协程。让我们来看看一个简单的例子: + +```go +package main +import ( + "fmt" + "time" +) +func main() { + go c() + fmt.Println("I am main") + time.Sleep(time.Second * 2) +} +func c() { + time.Sleep(time.Second * 2) + fmt.Println("I am concurrent") +} +//=> I am main +//=> I am concurrent +``` + +正如你在上面的例子中所看到的,函数 c 是一个 Go 例程,与 Go 主线程并行执行。有些时候,我们希望在多个线程之间共享资源。Go 倾向于不将一个线程的变量与另一个线程共享,因为这样会增加死锁和资源等待的可能性。还有一种方法可以在 Go 协程之间共享资源:通过 Go channels。 + +**Channels (通道)** + +我们可以使用通道在两个 Go 协程之间传递数据。在创建 channel 时,有必要指定该 channel 接收什么样的数据。让我们创建一个简单的字符串类型的 channel,如下所示: + +```go +c := make(chan string) +``` + +通过这个 channel,我们可以发送字符串类型的数据。我们可以在这个 channel 中发送和接收数据: + +```go +package main + +import "fmt" + +func main(){ + c := make(chan string) + go func(){ c <- "hello" }() + msg := <-c + fmt.Println(msg) +} +//=>"hello" +``` + +接收方 channel 等待,直到发送方发送数据到 channel。 + +**One way channel (单向通道)** + +有些情况下,我们希望 Go 程序通过 channel 接收数据,但不发送数据,反之亦然。为此,我们也可以创建一个**单向 channel**。让我们来看看一个简单的例子: + +```go +package main + +import ( + "fmt" +) + +func main() { + ch := make(chan string) + + go sc(ch) + fmt.Println(<-ch) +} + +func sc(ch chan<- string) { + ch <- "hello" +} +``` + +在上面的例子中,`sc` 是一个只用于发送消息到通道但不能接受消息的 go 协程。 + +### Organizing multiple channels for a Go routine using select (使用 select 为 Go 协程组织多个通道) + +一个函数可能有多个 channel 在等待。为此,我们可以使用一个选择(select)语句。让我们看一个例子,以了解更清楚的情况: + +```go +package main + +import ( + "fmt" + "time" +) + +func main() { + c1 := make(chan string) + c2 := make(chan string) + go speed1(c1) + go speed2(c2) + fmt.Println("The first to arrive is:") + select { + case s1 := <-c1: + fmt.Println(s1) + case s2 := <-c2: + fmt.Println(s2) + } +} + +func speed1(ch chan string) { + time.Sleep(2 * time.Second) + ch <- "speed 1" +} + +func speed2(ch chan string) { + time.Sleep(1 * time.Second) + ch <- "speed 2" +} +``` + +在上面的例子中,main 正在等待两个 channel,c1 和 c2。通过 select case 语句,main 函数打印出,信息从它先收到的 channel 中发送出来。 + +**Buffered channel(带缓冲的通道)** + +你可以在 go 中创建一个缓冲 channel。有了缓冲 channel,如果缓冲区满了,发送到该 channel 的消息就会被阻断。让我们看一下这个例子: + +```go +package main + +import "fmt" + +func main(){ + ch := make(chan string, 2) + ch <- "hello" + ch <- "world" + ch <- "!" // extra message in buffer + fmt.Println(<-ch) +} + +// => fatal error: all goroutines are asleep - deadlock! +``` + +正如我们在上面看到的,一个 channel 接受的信息不超过 2 条。 + +#### 为什么 Golang 会成功? + +> 简洁性…… — Rob-pike + +### Great + +我们学习了 Go 的一些主要组成部分和特点。 + +1. 变量、数据类型 +2. 数组 切片 和 映射 +3. 函数 +4. 循环和条件语句 +5. 指针 +6. 软件包 +7. 方法、结构体和接口 +8. 错误处理 +9. 并发 - Go 协程和通道 + +恭喜你,你现在对 Go 有了相当的了解。 + +> 我最有成效的一天是减少了 1000 行代码。 +> — Ken Thompson + +不要停在这里。继续向前推进。思考一个小的应用并开始创建。 + +[LinkedIn](https://www.linkedin.com/in/milap-neupane-99a4b565/), [Github](http://github.com/milap-neupane), [Twitter](https://twitter.com/_milap) + +也发布在 Milap Neupane 博客: [学习 Go,从 0 到 1](https://milapneupane.com.np/2019/07/06/learning-golang-from-zero-to-hero/) diff --git a/chinese/articles/procedural-macros-in-rust.md b/chinese/articles/procedural-macros-in-rust.md index 27fd7aa90..2d10047aa 100644 --- a/chinese/articles/procedural-macros-in-rust.md +++ b/chinese/articles/procedural-macros-in-rust.md @@ -296,9 +296,9 @@ This does create a problem though. You need to be able to make sense of this "st We can, however, rely on great open source work done by many developers to ease this for us. You need to add a few dependencies to help with this problem: -- `syn` — A syntax parser for Rust. This helps you to parse the input token stream as Rust AST. AST is a concept that you mostly run into when trying to write your own interpreter or compiler, but a basic understanding is essential for working with macros. Macros, after all, are just extensions that you write for the compiler in a sense. If you’re interested in learning more about what ASTs are, [check out this very helpful introduction][57]. -- `quote` — quote is, and this is a huge generalisation, a crate that helps us perform the reverse operation of what `syn` does. It helps us convert Rust source code into a stream of tokens that we can output from our macro. -- `proc-macro2` — The standard library provides a `proc-macro` crate, but the types it provides cannot exist outside of procedural macros. `proc-macro2` is a wrapper around the standard library that makes all of the internal types usable outside of the context of macros. This, for example, allows both `syn` and `quote` to not only be used for procedural macros, but in regular Rust code as well, should you ever have such a need. And we will indeed be using that extensively if we ever want to unit test our macros or their expansions. +- `syn`—A syntax parser for Rust. This helps you to parse the input token stream as Rust AST. AST is a concept that you mostly run into when trying to write your own interpreter or compiler, but a basic understanding is essential for working with macros. Macros, after all, are just extensions that you write for the compiler in a sense. If you’re interested in learning more about what ASTs are, [check out this very helpful introduction][57]. +- `quote`—quote is, and this is a huge generalisation, a crate that helps us perform the reverse operation of what `syn` does. It helps us convert Rust source code into a stream of tokens that we can output from our macro. +- `proc-macro2`—The standard library provides a `proc-macro` crate, but the types it provides cannot exist outside of procedural macros. `proc-macro2` is a wrapper around the standard library that makes all of the internal types usable outside of the context of macros. This, for example, allows both `syn` and `quote` to not only be used for procedural macros, but in regular Rust code as well, should you ever have such a need. And we will indeed be using that extensively if we ever want to unit test our macros or their expansions. - `darling`–It facilitates parsing and working with macro arguments, which is otherwise a tedious process due to having to manually parse it from the syntax tree. `darling` provides us with `serde`\-like ability to automatically parse input argument tree into our arguments struct. It also helps us in error handling around invalid arguments, required arguments, and so on. While these projects are contributed to by many developers, I want to give special thanks to [David Tolnay][58]. He's a legend in the Rust community and is the creator of most of these projects, and many many more open source crates in Rust.