BEGINNING RUST PROGRAMMING

Author: Ric Messier

如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的

Chapter 8 Going Relational

  • 关于不同类型的数据库平台
  • 在Rust中如何操作数据库
  • 如何使用多个源文件
  • 关于应用架构

APPLICATION ARCHITECTURES

我们将讨论三种不同的应用程序架构,以让您了解如何从多个角度考虑应用程序开发。首先是大规模应用程序设计的经典方法,通常称为n层。当我们谈到使用数据库时,这一点尤其重要,因为这是web应用程序架构的一种常见方法。然而,并不是所有的现代web应用程序都使用n层应用程序。有些人已经或正在转移到面向服务的体系结构上,这在今天通常被称为微服务。我们将讨论微服务以及它们如何与应用程序设计之间的关系。最后,尽管如此,我们将讨论模型-视图-控制器(MVC),这是一种设计理念,特别用于为iOS和iPadOS等苹果平台编写的应用程序。

n-Tier Applications

虽然您可以在任何应用程序中使用n层设计,但在谈论web应用程序时可能是最容易想到的。例如,想想一个电子商务网站,它有一个你可以交流的界面,在那里你可以搜索产品,获取关于它们的信息,然后,最终,购买它们。对于这样的应用程序,您需要一个地方来存储有关产品的信息,比如数据库。您还需要一些元素来处理所有的业务逻辑,即所有的编程元素。最后,你需要一些能够与用户沟通的东西。这将是一个三层的设计。如果你在浏览器中添加了你要查看网站的地方,你可以认为它是一个四层设计

  • Presentation layer: 这是负责向用户呈现所有内容的层,包括从用户接收任何输入。对我们来说,这将包括用户的浏览器和web服务器,或者至少是web服务器的组件,因为web服务器负责提供在用户的浏览器中呈现的信息
  • Application layer: 这是所有业务逻辑发生的地方,这意味着所有的编程元素。应用层可以负责将数据格式化,以便可以将其传递回表示层以进行正确的显示。
  • Data access layer: 这是发送数据以持久化和检索数据的地方。

您可以很容易地看到这是如何应用于您有不同组件的web服务器的,即使它们只是应用程序,而不是不同的服务器。如何将其应用到本机应用程序设计中可能不那么容易看到。考虑这个问题的一种方法是总是有一个元素/模块/函数处理I/O。您不应该将所有的功能都放在应用程序中,它们直接与用户通信。确保你所有的I/O都发生在一个地方。这允许您为用户界面做其他事情,而不必更改应用程序中的每个函数。

数据持久性也是如此。不应该允许每个函数都直接与数据库服务器通信。处理所有这些事情的最好方法是通过使用接口。Rust中的struct为我们提供了创建接口的能力。例如,您构建了一个包含对output句柄的引用的struct,无论它是用于控制台接口的stdin/stdout还是文件句柄。附加到该结构上,创建可以以通用方式使用的traits。底层的I/O句柄或接口在哪个位置并不重要;作为程序员,您所调用的部分将总是相同的。

Microservices

这基本上就是微服务背后的理念。在此之前,你可能有一个类,用面向对象的术语来说,它提供了一个你可以看作是一种服务的函数,现在你可以开始把它看作是一种服务。此服务将被其他服务使用。每个微服务都将有自己独立的界面供他人交流,也将有本质上的合同:我,陈述你的名字,庄严宣誓,作为一种服务,我将向任何要求的人提供以下内容。然后,每一项服务都填补了他们将提供给外部世界的空白。这被编码到到服务的接口中——这意味着服务被调用的方式。

使用微服务设计的一个常见原因是,如果您正在使用移动应用程序。一些应用程序逻辑包含在移动应用程序中,但它可以接触到应用程序后端的一个基于web的实例来执行大量的工作。移动设备上的应用程序知道如何在云环境中接触到应用程序编程接口(API)网关。可以在图中看到,其中浏览器或移动应用程序可能会使用云中的API。所有的微服务,您可以在图中看到的一些,都是API中的端点。

应用程序唯一知道的是如何在基于云的应用程序中调用端点,类似于远程过程调用。所有的关键代码都在那里,所以它都在开发团队或提供整个应用程序的公司的控制之下。这使得更新它变得容易得多。不需要等待终端用户在其一端更新应用程序。在端点上运行的代码大多是无关紧要的。一旦代码准备好部署,就可以在基于云的实例上完成所有更新。

这并不是说你必须开发移动或基于web的应用程序来利用微服务模型。如果您有能力进行进程间通信,无论以任何方式,您都可以在本地应用程序中实现微服务,就像我们一直在研究的那样。您只需要一个组件能够与另一个组件对话的能力,并且有很多方法可以实现这一点。

Model-View-Controller

DATABASES

Structured Query Language

SQL基础知识(略)

Server or Embedded

开源数据库

  • MySQL
  • MariaDB
  • PostgreSQL

商业数据库

  • Oracle
  • SQL Server

嵌入式数据库

  • SQLite

Accessing Databases

一旦您掌握了连接如何工作以及创建连接和向服务器发送查询的机制,您就应该能够良好地交换不同的库来连接到不同的服务器。

首先是连接MySQL,使用的库名字也是mysql

use mysql::*;

fn main() {
    let connstr = "mysql://root:Passw0rd!@192.168.4.25:3306/stratapp";
    let sqlpool = Pool::new(connstr).unwrap();
    let connection = sqlpool.get_conn().unwrap();
    println!("{}", connection.connection_id());
}

WRITING A DATABASE PROGRAM

接下来会编写与Sqlite数据库有关的程序,需要提前准备好环境,使用的库的名字也是sqlite

Main and Modules

在Rust中使用结果的问题之一是,处理结果值需要花费大量的附加代码,从而使其更难读取。此外,在许多(如果不是大多数)的情况下,您不想对结果做任何特别的事情,所以最好让语言处理结果情况,并返回您真正想要的值

use sqlite;
use sqlite::Connection;
use std::env;
use std::error::Error;
mod dbfuncs;

fn main() -> Result<(), Box<dyn Error>> {
    let conn = Connection::open("stratapp.db")?;

    conn.execute(
        "CREATE TABLE IF NOT EXISTS findings (
        findings_ID INTEGER PRIMARY KEY,
        title TEXT NOT NULL,
        finding TEXT NOT NULL,
        details TEXT,
        justification TEXT)")?;

    let args: Vec<String> = env::args().collect();

    if args.len() > 1 {
        let command: &str = &args[1];
        match command {
            "add" => dbfuncs::addrecord(&conn)?,
            "list" => dbfuncs::listrecords(&conn),
            _ => println!("Didn't send a valid command in")
        }
    }
    else {
        println!("Please specify add or list as a command line parameter");
    }

    Ok(())
}

mod dbfuncs,这句话告诉编译器,需要将这个模块读进来,有不同的方法来构造这个模块,包括创建一个具有模块名称的目录。最简单的方法,也就是我们将要的方法,是创建一个具有模块名的文件。这意味着在源代码目录中,有一个名为dbfuncs.rs的文件。只要该文件在那里,Rust编译器就会很高兴,并且只需从该文件中拉入所有代码,并将其与main.rs文件中的代码一起编译。

fn main() -> Result<(), Box<dyn Error>>,这告诉编译器,方法返回一个Result,而且两种类型也确定下来了。以这种方式包含返回类型的主要原因是为了能够更容易地处理由调用函数导致的错误

错误是特定于产生错误的库的,在某些情况下,它们是特定于产生错误的trait的。这意味着,如果我们调用几个可能产生不同错误的不同trait,那么错误的类型可能会有问题。这就是为什么我们想要保持尽可能多的通用性。我们在返回类型中声明错误的方式允许我们接受不同的错误类型并返回它们。

当我们尝试创建一个通用错误时,我们会遇到编译器需要做一些称为动态调度的事情的情况。通过动态调度,必须在运行时决定移动到什么代码或包含什么数据结构。换句话说,是动态的,而不是静态的。这将导致在运行时影响性能。

我们将要使用的是一个名为Box的通用构造,它会在堆上分配一个容器。堆是分配动态数据的地方,这意味着在编译时不知道的数据。在编译时已知的任何东西都会被分配到堆栈上。Box实际上是对内存块的引用,这意味着您可以将任何东西放入Box中,因为Box只是指向内存中一个位置的指针。我们需要告诉编译器什么将进入Box,所以我们告诉它我们将在Box中放一个错误。Box<dyn value>称为trait object。这是因为我们选择了一些通常是特定的东西,并使它变得通用。我们表示的不是一个trait的特定实现,而是表示我们将使用一个通用版本,并且特定版本将在运行时知道该版本。

Database Functions

use sqlite;
use sqlite::State;
use sqlite::Connection;
use std::io;

pub fn addrecord(conn: &Connection) -> io::Result<()> {
    let mut title = String::new();
    let mut finding = String::new();
    let mut details = String::new();
    let mut justification = String::new();
    println!("Title");
    io::stdin().read_line(&mut title)?;
    println!("Finding text");
    io::stdin().read_line(&mut finding)?;
    println!("Details of the finding");
    io::stdin().read_line(&mut details)?;
    println!("Justification");
    io::stdin().read_line(&mut justification)?;
    let commandstring = format!("INSERT INTO findings (title, finding, details,
    justification) VALUES (\"{}\",
    \"{}\", \"{}\", \"{}\")", title.trim(), finding.trim(), details.trim(),
    justification.trim());
    let _statement = conn.execute(&commandstring).unwrap();
    Ok(())
}

pub fn listrecords(conn: &Connection) {
    let mut statement = conn
        .prepare("SELECT * FROM findings")
        .unwrap();
    while let State::Row = statement.next().unwrap() {
        println!("-----------------------------");
        println!("Title = {}", statement.read::<String>(1).unwrap());
        println!("Finding = {}", statement.read::<String>(2).unwrap());
        println!("Details = {}", statement.read::<String>(3).unwrap());
        println!("Justification = {}", statement.read::<String>(4).unwrap());
    }
}

原文地址:http://www.cnblogs.com/huangwenhao1024/p/16927128.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性