Rust 定义结构体也是以struct关键字开头
#[derive(Debug)]
struct Rectangle {
length:u32,
width:u32,
user:String,
}
上面我们定义了结构体Rectangle,包含成员变量usert是String类型的,String在赋值时执行的是Move操作,如果将user值赋给其他变量会怎么样?
fn main() {
let rec1 = Rectangle{length:50,width:30,user:String::from("kingeasternsun")};
println!("{:#?}",rec1);
let s3 = rec1.user;
println!("{}",s3);
println!("{:#?}",rec1);
}
报错如下:
rec1中的user的值已经移动给s3了,所以
println!("{:#?}",rec1);
就非法了。
我们把
println!("{:#?}",rec1);
注释掉
再次执行
我们可以使用引用来避免move
let rec1 = Rectangle{length:50,width:30,user:String::from("kingeasternsun")};
println!("{:#?}",rec1);
let s3 = &rec1.user;
println!("{}",s3);
println!("{:#?}",rec1);
执行结果如下:
没有具体的字段名只有字段的类型
#[derive(Debug)]
struct Color(i32,i32,i32);
let black = Color(0,0,0);
println!("{:#?}",black);
打印结果:
我们可以定义一个没有任何字段的结构体,他们被成为类单元结构体
方法和函数类似,使用fn关键字 和名字声明,可以拥有参数和返回值。不过与函数不同的是,他们在结构体的上下文中被定义,并且他们的第一个参数总是self,代表方法被调用的结构体的实例(这个倒和c++十分类似)。
impl Rectangle{
fn area(&self)->u32{
self.length*self.width
}
}
调用该方法
println!(
"the area is {}",
rec1.area()
)
结果如下:
如果想要在方法中改变实例的值,需要将第一个参数改为&mut self,如下:
#[derive(Debug)]
struct Rectangle {
length:u32,
width:u32,
user:String,
}
impl Rectangle{
fn area(&self)->u32{
self.length*self.width
}
fn changew(&mut self,w:u32){
self.width = w
}
}
测试代码如下,注意rec1变量也要声明为mut:
fn main() {
let mut rec1 = Rectangle{length:50,width:30,user:String::from("kingeasternsun")};
println!("{:#?}",rec1);
// let s3 = &rec1.user;
// println!("{}",s3);
// println!("{:#?}",rec1);
// let black = Color(0,0,0);
// println!("{:#?}",black);
println!(
"the area is {}",
rec1.area()
);
rec1.changew(3);
println!("{:#?}",rec1);
println!(
"the area is {}",
rec1.area()
);
}
执行结果
在impl块中定义,不以self为参数的函数,该函数是关联函数,因为他们与结构体相关,但是他们时函数而不是方法,因为他们不作用于结构体的实例(类似c++中static成员函数)。
golang中声明结构体要使用关键字type 结构体名 struct
golang中,结构体的成员和方法的访问权限是根据成员名和方法名的首字母决定的,如果首字母时大写就是pulic的,如果是小写的就是private的。首字母小写的成员在转为json时也不会出现在json字符串中。
type Rectangle struct {
Length uint32
Width uint32
User string
}
func main() {
rec1 := Rectangle{Length: 50, Width: 30, User: "kingeastern"}
fmt.Println(rec1)
fmt.Printf("%#v\n", rec1)
s3 := rec1.User
fmt.Println(s3)
fmt.Println(rec1)
var rec2 Rectangle
rec2.Length = 1
rec2.Width = 3
rec2.User = "kingeasternsun"
fmt.Printf("%#v\n", rec2)
}
打印结果如下:
在golang中我们还可以声明匿名结构体
var stu struct {
Name string
Age int
}
stu.Age = 3
stu.Name = "kingeasternsun"
fmt.Printf("%#v\n", stu)
打印结果如下:
struct { Name string; Age int }{Name:"kingeasternsun", Age:3}
定义如下结构体
type Point struct{ X, Y int }
type Circle struct {
Point
Radus int
}
func main() {
var w Circle
w.X = 3 // 还可以写为 w.Point.X
w.Y = 4
w.Radus = 5
fmt.Printf("%#v\n", w)
}
打印结果如下:
main.Circle{Point:main.Point{X:3, Y:4}, Radus:5}
如果Point还实现了某个接口或方法,Circle的变量也可以直接调用该接口或方法,非常强大和实用。
在golang中不使用this或self作为receiver,而是使用receiver的名字。如下例子中给结构体Rectangle定义的方法Area
Nil是一个有效的Receiver 值.因为对于map或slice来说,nil是一个有效的0值。
func (r Rectangle) Area() uint32 {
return r.Length * r.Width
}
由于调用一个函数对参数执行复制操作,所以如果一个参数太大或者需要修改结构体内的成员的值,可以通过指针传递:
func (r *Rectangle) ChangeW(w uint32) {
r.Width = w
}
定义方法如下
type Rectangle struct {
Length uint32
Width uint32
User string
}
func (r Rectangle) Area() uint32 {
return r.Length * r.Width
}
func (r *Rectangle) ChangeW(w uint32) {
r.Width = w
}
测试代码如下:
func main() {
rec1 := Rectangle{Length: 50, Width: 30, User: "kingeastern"}
fmt.Printf("%#v\n", rec1)
fmt.Printf("area is %d\n", rec1.Area())
rec1.ChangeW(3)
fmt.Printf("%#v\n", rec1)
fmt.Printf("area is %d\n", rec1.Area())
}
结果打印如下:
如果我们加上如下代码:
Rectangle{Length: 50, Width: 30, User: "kingeastern"}.ChangeW(2)
编译报错
因为
Rectangle{Length: 50, Width: 30, User: "kingeastern"}.ChangeW(2)
首先得到了一个临时的Rectangle,这个临时值在golang里面是non-addressable的,也就是说无法获取这个临时值的地址,所以就没有办法调用ChangeW。
在golang中还可以给slice,number,strings,map等内置数据类型甚至某些函数定义方法:
type Path []int
func (p Path) Len()int{
return len(p)
}
在嵌入式结构体中利用unamed struct实现上传结构直接调用下层成员方法
var cache = struct {
sync.Mutex
mapping map[string]int
}{
mapping: make(map[string]int, 0),
}
func Lookup(key string) int {
cache.Lock()
v := cache.mapping[key]
cache.Unlock()
return v
}
var a struct{}