• rust数据库操作
    • 创建lib.rs文件

    rust数据库操作

    编程时,我们依赖数据库来存储相应的数据,很多编程语言都支持对数据库的操作,所以当然可以使用Rust操作数据库。

    不过在我自己操作时,发现很多问题,主要因为我不了解Rust在操作数据库时,应该注意的事情,从而浪费了很多的时间,在进行数据查询时。
    具体遇到的坑,我会做一些演示,从而让大家避免这些情况。

    首先使用Rust操作PostgreSQL,因为PostgreSQL是我最喜欢的数据库。

    首先创建新项目 cargo new db --bin

    在cargo.toml中添加 postgres 如下:

    1. [package]
    2. name = "db"
    3. version = "0.1.0"
    4. authors = ["vagrant"]
    5. [dependencies]
    6. postgres="*"

    当然我们还是进行最简单的操作,直接粘贴复制,代码来源

    1. extern crate postgres;
    2. use postgres::{Connection, SslMode};
    3. struct Person {
    4. id: i32,
    5. name: String,
    6. data: Option<Vec<u8>>
    7. }
    8. fn main() {
    9. let conn = Connection::connect("postgres://postgres@localhost", SslMode::None)
    10. .unwrap();
    11. conn.execute("CREATE TABLE person (
    12. id SERIAL PRIMARY KEY,
    13. name VARCHAR NOT NULL,
    14. data BYTEA
    15. )", &[]).unwrap();
    16. let me = Person {
    17. id: 0,
    18. name: "Steven".to_string(),
    19. data: None
    20. };
    21. conn.execute("INSERT INTO person (name, data) VALUES ($1, $2)",
    22. &[&me.name, &me.data]).unwrap();
    23. for row in &conn.query("SELECT id, name, data FROM person", &[]).unwrap() {
    24. let person = Person {
    25. id: row.get(0),
    26. name: row.get(1),
    27. data: row.get(2)
    28. };
    29. println!("Found person {}", person.name);
    30. }
    31. }

    这些简单的,当然不是我们想要的东西,我们想要的是能够进行一些分层,也就是
    基本的一些函数逻辑划分,而不是在一个main函数中,完成所有的一切。

    创建lib.rs文件

    从上到下来看文件:

    1. 首先导入postgres的各种库
    2. 创建一个Person 的struct,按照需求的字段和类型。
    3. 创建一个连接函数,返回连接对象。
    4. 创建一个插入函数,用来插入数据
    5. 创建一个查询函数,用来查询数据
    6. 创建一个查询函数,用来查询所有的数据。

    当然这些函数都是有一定的功能局限性。

    1. extern crate postgres;
    2. use postgres::{Connection, SslMode};
    3. use postgres::types::FromSql;
    4. use postgres::Result as PgResult;
    5. struct Person {
    6. id: i32,
    7. name: String,
    8. data: Option<Vec<u8>>
    9. }
    10. pub fn connect() -> Connection{
    11. let dsn = "postgresql://postgres:2015@localhost/rust_example";
    12. Connection::connect(dsn, SslMode::None).unwrap()
    13. }
    14. pub fn insert_info(conn : &Connection,title : &str, body: &str){
    15. let stmt = match conn.prepare("insert into blog (title, body) values ($1, $2)") {
    16. Ok(stmt) => stmt,
    17. Err(e) => {
    18. println!("Preparing query failed: {:?}", e);
    19. return;
    20. }
    21. };
    22. stmt.execute(&[&title, &body]).expect("Inserting blogposts failed");
    23. }
    24. pub fn query<T>(conn: &Connection,query: &str) ->PgResult<T>
    25. where T: FromSql {
    26. println!("Executing query: {}", query);
    27. let stmt = try!(conn.prepare(query));
    28. let rows = try!(stmt.query(&[]));
    29. &rows.iter().next().unwrap();
    30. let row = &rows.iter().next().unwrap();
    31. //rows.iter().next().unwrap()
    32. row.get_opt(2).unwrap()
    33. }
    34. pub fn query_all(conn: &Connection,query: &str){
    35. println!("Executing query: {}", query);
    36. for row in &conn.query(query,&[]).unwrap(){
    37. let person = Person{
    38. id: row.get(0),
    39. name: row.get(1),
    40. data: row.get(2)
    41. };
    42. println!("Found person {}", person.name);
    43. }
    44. }

    然后在main.rs 中调用相应的函数代码如下

    1. extern db ,引入db,也就是将项目本身引入
    2. use db 使用db,中的可以被引入的函数
    3. 定义Blog,由于个人使用blog表,是自己创建,所以如果报错说不存在表,需要你自己去创建
    4. 使用lib中定义的函数,进行最基本的一些操作
    1. extern crate postgres;
    2. extern crate db;
    3. use postgres::{Connection, SslMode};
    4. use db::*;
    5. struct Blog {
    6. title: String,
    7. body: String,
    8. }
    9. fn main() {
    10. let conn:Connection=connect();
    11. let blog = Blog{
    12. title: String::from("title"),
    13. body: String::from("body"),
    14. };
    15. let title = blog.title.to_string();
    16. let body = blog.body.to_string();
    17. insert_info(&conn,&title,&body);
    18. for row in query::<String>(&conn,"select * from blog"){
    19. println!("{:?}",row);
    20. }
    21. let sql = "select * from person";
    22. query_all(&conn,&sql);
    23. }

    自己遇到的坑

    • 创建连接函数时,连接必须有一个返回值,所以必须指定返回值的类型,
      对于一个写Python的人而言,我觉得是痛苦的,我想按照官方的写法match
      一下,发现可能产生多个返回值。在编译时直接无法通过编译,所以最终
      使用了unwrap,解决问题,不过我还是没有学会,函数多值返回时我如何
      定义返回值

    • 在使用&conn.query(query,&[]).unwrap()时,我按照文档操作,文档说
      返回的是一个可迭代的数据,那也就是说,我可以使用for循环,将数据打印,
      但是发现怎么也不能实现:

    1. pub fn query_all(conn: &Connection,query: &str){
    2. println!("Executing query: {}", query);
    3. for row in &conn.query(query,&[]).unwrap(){
    4. println!("Found person {:?}", row.get_opt(1));
    5. }
    6. }

    报错如下:

    1. vagrant@ubuntu-14:~/tmp/test/rustprimer/db$ cargo run
    2. Compiling db v0.1.0 (file:///home/vagrant/tmp/test/rustprimer/db)
    3. src/lib.rs:53:37: 53:47 error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
    4. src/lib.rs:53 println!("Found person {:?}", row.get_opt(1));
    5. ^~~~~~~~~~
    6. <std macros>:2:25: 2:56 note: in this expansion of format_args!
    7. <std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>)
    8. src/lib.rs:53:3: 53:49 note: in this expansion of println! (defined in <std macros>)
    9. src/lib.rs:53:37: 53:47 help: run `rustc --explain E0282` to see a detailed explanation
    10. error: aborting due to previous error
    11. Could not compile `db`.

    然后去查看了关于postgres模块的所有函数,尝试了无数种办法,依旧没有解决。

    可能自己眼高手低,如果从头再把Rust的相关教程看一下,可能很早就发现这个问题,
    也有可能是因为习惯了写Python,导致自己使用固有的思维来看待问题和钻牛角尖,才
    导致出现这样的问题,浪费很多的时间。

    • 改变思维,把自己当作一个全新的新手,既要利用已有的思想来学习新的语言,同样不要
      被自己很精通的语言,固化自己的思维。