Structs类似于ts中的Interface,姑且称之为结构吧。在rust中它跟tuple非常相似。与元组不同的是,我们可以为每个数据命名,以便清楚地知道这些值的含义。 由于使用了这些名称,因此结构比元组更灵活:不必依赖数据的顺序来指定或访问实例的值。

废话少说,先来一个structs的实例:

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
要定义一个结构,我们输入关键字struct并命名整个结构。 结构的名称应说明将数据分组在一起的重要性。 然后,在大括号内,定义数据片段的名称和类型,我们将其称为字段。

struct的使用,非常类似于interface,比如使用上面定义的User,类似于ts中定于一个特定的对象:

#[derive(Debug)]
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
fn main() {
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    
    println!("{:?}", user1)
}
cargo run
    User { username: "someusername123", email: "someone@example.com", sign_in_count: 1, active: true }
注意想要把结构实例打印出来必须在使用struct的上面添加一行#[derive(Debug)],否则就会报错。

那么,如何使用和改变一个结构实例中的属性呢?看实例:

println!("{:?}", user1.email);

//如果定义的实例是一个可变变量的话,那么可以改变其属性值。
//let mut user1 = User {...}
user1.email = String::from("test@example.com");
println!("{:?}", user1.email);

cargo run
    "someone@example.com"
    "test@example.com"

是否可以向js中的工厂方法来获取实例呢,可以的,例如:

fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}

fn main() {
    let user1 = build_user(
        String::from("someone@example.com"),
        String::from("someusername123"),
    );
    
    println!("{:?}", user1)
}

cargo run
    User { username: "someusername123", email: "someone@example.com", sign_in_count: 1, active: true }
注意想要改变实例的某个属性的时候整个实例必须是可变的。 Rust不允许我们仅将某些字段标记为可变字段。 与任何表达式一样,我们可以构造该结构的新实例作为函数主体中的最后一个表达式,以隐式返回该新实例。

特别,如果构造方法中含有固定的属性值,且当变量和字段具有相同名称时,使用字段初始化速记:

fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}

fn main() {
    let user1 = build_user(
        String::from("someone@example.com"),
        String::from("someusername123"),
    );
}

在上述例子中,我们正在创建User结构的新实例,该实例具有一个名为email的属性, 我们想将email属性的值设置为build_user函数的email参数中的值。 因为email字段和email参数具有相同的名称,所以我们只需要编写email而不是email:email。

此外我们还可以使用结构更新语法从其他实例创建实例

fn main() {
    let user1 = build_user(
        String::from("someone@example.com"),
        String::from("someusername123"),
    );

    let user2 = User {
        email: String::from("another@example.com"),
        username: String::from("anotherusername567"),
        active: user1.active,
        sign_in_count: user1.sign_in_count,
    };
    println!("{:?}", user1);
    println!("{:?}", user2);
}

cargo run
    User { username: "someusername123", email: "someone@example.com", sign_in_count: 1, active: true }
    User { username: "anotherusername567", email: "another@example.com", sign_in_count: 1, active: true }

另外,使用struct update语法,我们可以用更少的代码来达到相同的效果,如下实例用法。 语法..指定未明确设置的其余字段应与给定实例中的字段具有相同的值:

//与上例的结果一模一样
let user2 = User {
        email: String::from("another@example.com"),
        username: String::from("anotherusername567"),
        ..user1
  };

使用没有命名字段的元组结构创建不同的类型

我们还可以定义看起来类似于元组的结构,称为元组结构。元组结构具有附加的含义,即结构名称提供的含义,但没有与其字段关联的名称; 相反,它们只是字段的类型。 当我们想给整个元组起一个名字并使元组成为与其他元组不同的类型时,元组结构很有用,并且像常规结构中那样命名每个字段都是冗长或多余的。

要定义元组结构,与上面实例类似,以struct关键字和结构名称开头,后跟元组中的类型。 例如,以下是两个名为Color和Point的元组结构的定义和用法:

#[derive(Debug)]
struct Color(i32, i32, i32);
#[derive(Debug)]
struct Point(i32, i32, i32);
fn main() {
   let black = Color(0, 0, 0);
   let origin = Point(0, 0, 0);

    println!("{:?}", black);
    println!("{:?}", origin);
}

cargo run
    Color(0, 0, 0)
    Point(0, 0, 0)
    

注意,black和origin是不同的类型,因为它们是不同元组结构的实例。我们定义的每个结构都是其自己的类型,即使该结构中的字段具有相同的类型。 例如,即使两个类型都由三个i32值组成,但是采用Color类型参数的函数也不能将Point作为参数。因此,元组struct实例的行为就像元组:可以将它们分解为单独的片段,可以使用。然后用索引以访问单个值,依此类推。

没有任何字段的类似单元的结构

我们还可以定义没有任何字段的结构! 这些被称为类单元结构,因为它们的行为类似于单元类型()。 在需要在某种类型上实现特征但又不想在类型本身中存储任何数据的情况下,类似单元的结构很有用。

结构数据的所有权

在上述例子的User struct定义中,我们使用拥有的String类型而不是&str字符串切片类型。 其原因是我们希望该结构的实例拥有其所有数据,并且只要整个结构有效,该数据就一直有效。

结构可能会存储对其他人拥有的数据的引用,但这样做需要使用生命周期。作为结构生命周期可确保结构所引用的数据在很长的时间内有效。假设我们尝试将引用存储在结构中,但未指定生命周期,例如这样,将无效:

struct User {
    username: &str,
    email: &str,
    sign_in_count: u64,
    active: bool,
}

fn main() {
    let user1 = User {
        email: "someone@example.com",
        username: "someusername123",
        active: true,
        sign_in_count: 1,
    };
}
$ cargo run
  |
2 |     username: &str,
  |               ^ expected lifetime parameter

error[E0106]: missing lifetime specifier
 --> src/main.rs:3:12
  |
3 |     email: &str,
  |            ^ expected lifetime parameter

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0106`.
error: could not compile `structs`.

To learn more, run the command again with --verbose.

子康
10 声望0 粉丝