【Rust】ベクタ(vector)

ベクタは型の異なる要素を含めることはできない。要素数は可変。インデックスに変数を使用することができる。

fn main(){
    let mut vect = vec![10, 20, 30];
    vect.push(40);
    println!("{} {} {} {}", vect[0], vect[1], vect[2], vect[3]);

    for v in &vect {
        println!("{}", v);
    }
}

C++

#include <vector>
#include <stdio.h>

int main(void)
{
    std::vector<int> vec{1,2,3};

    for(int i = 0; i < vec.size(); i++)
        printf("%d", vec[i]);
    printf("\n");

    return 0;
}
#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> words = {"apple", "bird", "cat"};

    std::cout << words.size() << '\n';

    for(const auto& word : words)
    {
        std::cout << word << ' ';
    }

    std::cout << '\n';
}

rustの場合はsizeではなくlenで書く。

fn main(){
    let words = vec!["apple", "bird", "cat"];

    println!("{}", words.len());

    for w in &words {
        println!("{}", w);
    }
}

【Rust】配列

配列はTという単一の型のオブジェクトの集合。それらのオブジェクトはメモリ上の連続した領域に保存される。

fn main(){
    let arr = [10, 20, 30];
    println!("{}, {}, {}", arr[0], arr[1], arr[2]);

    for v in &arr {
        println!{"{}", v};
    }
}

C++の配列

#include <iostream>

void PrintArray1(const int x[5]){
    static_assert(sizeof(x) == sizeof(int*), "");
    for (int i = 0; i < 5; ++i){
        std::cout << x[i] << std::endl;
    }
}

void PrintArray2(const int* x) {
    for (int i = 0; i < 5; ++i){
        std::cout << x[i] << std::endl;
    }
}

int main()
{
    int x[5] = {0, 1, 2, 3, 4};

    PrintArray1(x);
    PrintArray2(x);

    return 0;
}

↓ Rustで書くことこうなる

fn PrintArray1(ary: [i32; 5]) {
    for i in ary {
        println!{"{}", i};
    }
}

fn main(){
    let x: [i32; 5]= [0, 1, 2, 3, 4];

    PrintArray1(x);
}

関数の引数は明確に定義しないといけない。

【Rust】タプル

タプルは異なる型の値の集合。括弧を用いて生成する。

fn main(){
    let tup = (10, "20", 30);
    println!("{}, {}, {}", tup.0, tup.1, tup.2);
}

C++でタプルを使用する場合は、型を指定しなければいけないが、Rustの場合は型指定がない。つまり自動で型推論をやってくれている?

#include <iostream>
#include <string>
#include <tuple>
#include <vector>

int main()
{
    std::tuple<int, double, std::string> t1{100, 1.1, "aaa"};
    std::tuple<std::string, int, int> t2{"bbb", 200, 300};
    std::vector<std::tuple<std::string, int, int>> v =
    {
        {"ccc", 400, 500},
        {"ddd", 600, 700}
    };
}

Rustではインデックスを用いて値にアクセスできるが、以下のような書き方をするとエラーとなる。

fn main(){
    let tup = (1, 1.2, "tupple");
    for n in 0..2 {
        println!("{}", tup.n);
    }
}

$ rustc main.rs
error[E0609]: no field `n` on type `({integer}, {float}, &str)`
–> main.rs:4:28
|
4 | println!(“{}”, tup.n);
| ^ unknown field

error: aborting due to 1 previous error

For more information about this error, try `rustc –explain E0609`.

また、型推論できない場合は、型を明示する必要がある。

【Rust】列挙型

### 定数値として扱う

enum Color {
    Red = 0x0000FF,
    Green = 0x00FF00,
    Blue = 0xFF0000,
}
fn main(){
    let color = Color::Red;
    println!("{}", color as usize);
}

### 特定の値を指定しない時

enum Color {
    Red,
    Green,
    Blue,
}
fn main(){
    let color = Color::Red as usize;
    println!("{}", color);
}

### 値を持つ列挙型

enum Color {
    RGB(u8, u8, u8),
    RGBA(u8, u8, u8, u8),
}
fn main(){
    let c = Color::RGB(0, 0, 255);
    let code = match c {
        Color::RGB(r, g, b) => b as u32 + ((g as u32) << 8) + ((r as u32) << 16),
        Color::RGBA(r, g, b, _) => b as u32 + ((g as u32) << 8) +  ((r as u32) << 16),   
    };
    println!("{}", code);
}

### 値を持つenumを比較

#[derive(Debug,PartialEq)]
enum Animal {
    Dog(String),
    Cat(String),
    Monkey(String),
}

fn main(){
    let taro1 = Animal::Dog(String::from("Taro"));
    let taro2 = Animal::Dog(String::from("Taro"));
    assert_eq!(taro1, taro2);

    let taro3 = Animal::Cat(String::from("Taro"));
    let taro4 = Animal::Dog(String::from("Taro"));
    assert_eq!(taro3, taro4);

    let taro5 = Animal::Monkey(String::from("Taro"));
    let taro6 = Animal::Monkey(String::from("Taro"));
    // println!(taro5 as usize, taro6 as usize);
}

値を持つ、持たないで扱いが異なるのね。。
列挙型と構造体は何となく似ているが、列挙型は名前を列挙していくだけなので、型指定はしない。

【Rust】構造体と共用体

### 構造体

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 100, y: 200};
    println!("{} {}", p.x, p.y);
}

### 共用体
共用体は同じメモリを使用する

union MyUnion {
    f1: u32,
    f2: u32,
}

fn main() {
    let u = MyUnion {f1: 123};
    unsafe {
        println!("{}", u.f1);
        println!("{}", u.f2);
    }
}

$ ./main
123
123

【Rust】Ubuntu24.04にRustをインストールする

### rustが入っていない状態
$ cargo –version
bash: cargo: command not found

### インストール
$ cd $home
$ sudo apt-get update
$ sudo apt-get install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang cmake make libprotobuf-dev protobuf-compiler
$ curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh
$ source $HOME/.cargo/env

### 最新バージョンの確認
$ rustup component add rustfmt
$ rustup update

### インストール確認
$ cargo –version
cargo 1.83.0 (5ffbef321 2024-10-29)
$ rustc –version
rustc 1.83.0 (90b35a623 2024-11-26)

rustのtraitとは

traitとは構造体に共通のメソッドを実装したい時に利用する

trait Animal {
	fn get_name(&self) -> &String;

	fn get_age(&self) -> usize {
		return 0;
	}
}

struct Cat {
	name: String,
	age: usize,
}

impl Animal for Cat {
	fn get_name(&self) -> &String {
		return &self.name;
	}
}

const cat = Cat {
	name:String::from("Tama"),
	age: 20,
};
println!("{}", cat.get_name());

他の言語のインターフェイス

axum sqlx

use sqlx::postgres::PgPoolOptions;

#[async_std::main]
async fn main() -> Result<(), sqlx::Error> {

	let pool = PgPoolOptions::new()
		.max_connections(5)
		.connect("postgres://postgres:password@localhost/test").await?;

	let row: (i64,) = sqlx::query_as("SELECT $1")
		.bind(150_i64)
		.fetch_one(&pool).await?;

	assert_eq!(row.0, 150);
	Ok(())
}


pub async fn startup(modules: Arc<Modules>){
	let hc_router = Router::new()
		.route("/", get(hc))
		.route("/postgres", get(hc_postgres));

	let todo_router = Router::new()
		.route("/", get(find_todo).post(create_todo))
		.route(
			"/:id",
			get(get_todo)
				.patch(update_todo)
				.put(upsert_todo)
				.delete(delte_todo),
		);

	let app = Router::new()
		.nest("/v1/hc", hc_router)
		.nest("/v1/todos", todo_router)
		.layer(Extension(modules));

	axum::Server::bind(&&addr)
		.server(app.into_mmake_service())
		.await
		.unwrap_or_else(|_| panic!("server cannot launch."));
}

use std::sync::Arc;
use todo_adapter::modules::{RepositoriesModule, RepositoriesModuleExt};
use todo_adapber::persistence::postgres::Db;
use todo_adapter::repository::health_check::HealthCheckRepository;
use todo_app::usecase::health_check::HealthCechkUseCase;
use todo_app::usecase::todo::TodoUseCase;

pub struct Modules {
	health_check_use_case:: HealthCheckUseCase,
	todo_use_case: TodoUseCase<RepositoriesModule>,
}

pub trait ModulesExt {
	type RepositoriesModule: RepositoriesModuleExt;

	fn health_check_use_case(&self) -> &HealthCheckUseCase;
	fn todo_use_case(&self) -> &TodoUseCase<Self::RepositoriesModule>;
}

immpl ModulesExt for Modules {
	type RepositoriesModule = RepositoriesModule;

	fn health_check_use_case(&self) -> &HealthCheckUseCase {
		&self.health_check_use_case
	}

	fn todo_use_case(&self) -> &TodoUseCase<Self::RepositoriesModule> {
        &self.todo_use_case
    }
}

impl Modules {
	pub async fn new() -> Self {
		pub async fn new() -> Self {
			let db = Db::new().await;

			let repositories_module = Arc::new(RepositoriesModule::new(db.clone()));

			let health_check_use_case = HealthCheckUseCase::new(HealthCheckRepository::new(db));
			let todo_use_case = TodoUseCase::new(repositories_module.clone());

			Self {
				health_check_use_case,
				todo_use_case,
			}
		}
	}
}


#[derive(Debug, Deserialize)]
#[serde(rename_all) = "camelCase"]
pub struct TodoQuery {
	pub status: Option<String>,
}

immpl From<TodoQuery> for SearchTodoCondition {
	fn from(tq: TodoQuery) -> Self {
		Self {
			status_code: tq.status,
		}
	}
}

pub async fn update_todo(
	Path(id): Path<String>,
	ValidatedRequest(source): ValidatedRequest<JsonUpdateTodoContents>,
	Extension(modules): Extension<Arc<Modules>>,
) -> Result<impl IntoResponse, impl IntoResponse> {
	match source.validate(id) {
		Ok(todo) => {
			let resp = modules.todo_use_case().update_todo(todo).await;

			resp.map(|tv| {
				info!("updated todo {}", tv.id);
				let json: JsonTodo = tv.info();
				(StatusCode::OK, Json(json))
			})
			.map_err(|err|{
				error!("{:?}", err);

                if err.to_string() == *"`statusCode` is invalid." {
                    let json = JsonErrorResponse::new(
                        "invalid_request".to_string(),
                        vec![err.to_string()],
                    );
                    (StatusCode::BAD_REQUEST, Json(json))
                } else {
                    let json = JsonErrorResponse::new(
                        "server_error".to_string(),
                        vec!["INTERNAL SERVER ERROR".to_string()],
                    );
                    (StatusCode::INTERNAL_SERVER_ERROR, Json(json))
                }
			})
		}
		Err(errors) => {
			let json = JsonErrorResponse::new("invalid_request".to_string(), errors);
			Err((Statuscode::BAD_REQUEST, Json(json)))
		}
	}
}

use crate::context::errors::AppError;
use crate::context::validate::ValidatedRequest;
use axum::async_trait;
use axum::extract::{FromRequest, RequestParts};
use axum::{BoxError, Json};
use serde::de::DeserializeOwned;
use validator::Validate;

#[async_trait]
impl<T, B> FromRequest<B> for ValidatedRequest<T>
where
    T: DeserializeOwned + Validate,
    B: http_body::Body + Send,
    B::Data: Send,
    B::Error: Into<BoxError>,
{
    type Rejection = AppError;

    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
        let Json(value) = Json::<T>::from_request(req).await?;
        value.validate()?;
        Ok(ValidatedRequest(value))
    }
}

use crate::context::errors::AppError;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::Json;
use serde::Serialize;
use tracing::log::error;

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct JsonErrorResponse {
    error_code: String,
    errors: Vec<String>,
}

impl JsonErrorResponse {
    pub(crate) fn new(error_code: String, errors: Vec<String>) -> Self {
        Self { error_code, errors }
    }
}

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        match self {
            AppError::Validation(validation_errors) => {
                error!("{:?}", validation_errors);

                let mut messages: Vec<String> = Vec::new();
                let errors = validation_errors.field_errors();
                for (_, v) in errors.into_iter() {
                    for validation_error in v {
                        if let Some(msg) = validation_error.clone().message {
                            messages.push(msg.to_string());
                        }
                    }
                }

                (
                    StatusCode::BAD_REQUEST,
                    Json(JsonErrorResponse::new(
                        "invalid_request".to_string(),
                        messages,
                    )),
                )
            }
            AppError::JsonRejection(rejection) => {
                error!("{:?}", rejection);

                let messages = vec![rejection.to_string()];
                (
                    StatusCode::BAD_REQUEST,
                    Json(JsonErrorResponse::new(
                        "invalid_request".to_string(),
                        messages,
                    )),
                )
            }
        }
        .into_response()
    }
}

#[derive(Deserialize, Debug, Validate)]
#[serde(rename_all = "camelCase")]
pub struct JsonCreateTodo {
    #[validate(
        length(min = 1, message = "`title` is empty."),
        required(message = "`title` is null.")
    )]
    pub title: Option<String>,
    #[validate(required(message = "`description` is null."))]
    pub description: Option<String>,
}

impl From<JsonCreateTodo> for CreateTodo {
    fn from(jc: JsonCreateTodo) -> Self {
        CreateTodo {
            title: jc.title.unwrap(),
            description: jc.description.unwrap(),
        }
    }
}

use sqlx::postgres::PgPoolOptions;
use sqlx::{Pool, Postgres};
use std::env;
use std::sync::Arc;

#[derive(Clone)]
pub struct Db(pub(crate) Arc<Pool<Postgres>>);

impl Db {
	pub async fn new() -> Db {
		let pool = PgPoolOptions::new()
			.max_connections(8)
			.connect(
				&env::var("DATABASE_URL").unwrap_or_else(|_| panic!("DATABASE_URL must be selt!")),
			)
			.await
			.unwrap_or_else(|_| {
				panic!("Cannot connect to the database. Please check your configuration.")
			});

		Db(Arc::new(pool))	
	}
}

async fn get(&self, id: &Id<Todo>) -> anyhow::Result<Option<Todo>> {
	let pool = self.db.0.clone();
	let sql = r#"
		select 
			t.id as id,
			t.title as title,
			t.description as description,
			ts.id as status_id,
			ts.code as status_code,
			ts.name as status_name,
			t.created_at as created_at,
			t.updated_at as updated_at
		from
			todos as t
			inner join
				todo statuses as ts
				on ts.id = t.status_id
		where
			t.id = $1
	"#;
	let stored_todo = query_as::<_, StoredTodo>(sql)
		.bind(id.value.to_string())
		.fetch_one(&*pool)
		.await
		.ok();

	match stored_todo {
		Some(st) => Ok(Some(st.try_into()?)),
		None => Ok(None),
	}

	"
}

Rust axumでcrudを実装する

$ cargo new axum_crud_api

Cargo.toml

[package]
name = "axum_crud_api"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
axum = "0.5.9"
tokio = { version="1.0", features = ["full"] }
serde = "1.0.137"
tracing = "0.1"
tracing-subscriber = { version="0.3", features = ["env-filter"] }

sqlx = { version = "0.5", features = ["runtime-tokio-tls", "json", "postgres"] }
anyhow = "1.0.58"
serde_json = "1.0.57"
tower-http = { version="0.3.4", features = ["trace"] }

main.rs

use axum::{
	routing::{get},
	Router,
};

use std::net::SocketAddr;

#[tokio::main]
async fn main() {
	let app = Router::new()
		.route("/", get(root));

	let addr = SocketAddr::from(([192,168,56,10], 8000));
	println!("listening on {}", addr);
	axum::Server::bind(&addr)
		.serve(app.into_make_service())
		.await
		.unwrap();
}

async fn root() -> &'static str {
	"Hello, World"
}

task.rs

use serde::{Deserialize, Serialize};

pub struct Task {
	pub id: i32,
	pub task: String,
}

pub struct NewTask {
	pub task: String,
}

DATABASE_URL = postgresql://user:password@locahost:host/database

$ cargo install sqlx-cli
$ cargo install sqlx-cli –no-default-features –features native-tls,postgres
$ cargo install sqlx-cli –features openssl-vendored
$ cargo install sqlx-cli –no-default-features –features rustls

sqlx database create
sqlx migrate add task

CREATE TABLE task {
id SERIAL PRIMARY KEY,
task varch(255) NOT NULL
}

sqlx migrate run

main.rs

use axum::{
	extract::{Extension},
	routing::{get, post},
	Router,
};

use sqlx::postgres::PgPoolOptions;
use std::net::SocketAddr;
use std::fs;
use anyhow::Context

#[tokio::main]
async fn main() -> anyhow::Result<()> {

	let env = fs::read_to_string(".env").unwrap();
	let (key, database_url) = env.split_once('=').unwrap();

	assert_eq!(key, "DATABASE_URL");

	tracing_subscriber::fmt::init();

	let pool = PgPoolOptions::new()
	.max_connections(50)
	.connect(&dtabase_url)
	.await
	.context("could not connect to database_url")?;

	let app = Router::new()
		.route("/hello", get(root));

	let addr = SocketAddr::from(([192,168,56,10], 8000));
	println!("listening on {}", addr);
	axum::Server::bind(&addr)
		.serve(app.into_make_service())
		.await?;

		Ok(())
}

controller/task.rs

use axum::response::IntoResponse;
use axum::http::StatusCode;

use axum::{Extension, Json}
use sqlx::PgPool;

use crate::{
	models::task
};

pub async fn all_tasks(Extension(pool): Extension<PgPool>) -> impl IntoResponse {
	let sql = "SELECT * FROM task".to_string();

	let task = sqlx::query_as::<_, task::Task>(&sql).fetch_all(&pool).await.unwrap();

	(StatusCode::OK, Json(task))
}

error.rs

use axum::{http::StatusCode, response::IntoResponse, Json};
use serde_json::json;

pub enum CustomError {
	BadRequest,
	TaskNotFound,
	InternalServerError,
}

impl IntoResponse for CustomError {
	fn into_response(self) -> axum::response::Response {
		let (status, error_message) = match self {
			Self::InternalServerError => (
				StatusCode::INTERNAL_SERVER_ERROR,
				"Internal Server Error",
			),
			Self::BadRequest=> (StatusCode::BAD_REQUEST, "Bad Request"),
			Self::TaskNotFound => (StatusCode::NOT_FOUND, "Task Not Found"),
		};
		(status, Json(json!({"error": error_message}))).into_response()
	}
}

GET

// GET
pub async fn task(Path(id):Path<i32>,
	Extension(pool): Extension<PgPool>)-> Result<Json<task::Task>, CustomError>{

	let sql = "SELECT * FROM task where id=$1".to_string();

	let task: task::Task = sqlx::query_as(&sql)
		.bind(id)
		.fetch_one(&pool)
		.await
		.map_err(|_| {
			CustomError::TaskNotFound
		})?;

	Ok(Json(task))
}

// POST
pub async fn new_task(Json(task): Json<task::NewTask>,
	Extension(pool): Extension<PgPool>) -> Result<(StatusCode,
	Json<task::NewTask>), CustomError> {
		if task.task.is_empty() {
			return Err(CustomError::BadRequest)
		}
		let sql = "INSERT INTO task (task) values ($1)";

		let _ = sql::query(&sql)
			.bind(&task.task)
			.execute(&pool)
			.await
			.map_err(|_| {
				CustomError::InternalServerError
			})?;

		Ok((StatusCode::CREATED, Json(task)))
	}


// PUT
pub async fn update_task(Path(id): Path<i32>,
	Json(task): Json<task::UpdateTask>, Extension(pool): Extension<PgPool>)
	-> Result <(StatusCode, Json<task::UpdateTask>), CustomError> {

		let sql = "SELECT * FROM task where id=$1".to_string();

		let _find: task::Task = sqlx::query_as(&sql)
			.bind(id)
			.fetch_one(&pool)
			.await
			.map_err(|_| {
				CustomError::TaskNotFound
			})?;

		sqlx::query("UPDATE task SET task=$1 WHERE id=$2")
			.bind(&task.task)
			.bind(id)
			.execute(&pool)
			.await;

		Ok((StatusCode::OK, Json(task)))
	}

pub async fn delete_task(Path(id): Path<i32>, Extension(pool):Extension<PgPool>)
	-> Result<(StatusCode, Json<Value>), CustomError> {

		let _find: task::Task = sqlx::query_as("SELECT * FROM task where id=$1")
			.bind(id)
			.fetch_one(&pool)
			.await
			.map_err(|_| {
				CustomError::TaskNotFound
			})?;

		sqlx::query("DELETE FROM task WHERE id=$1")
			.bind(id)
			.execute(&pool)
			.await
			.map_err(|_|{
				CustomError::TaskNotFound
			})?;

			Ok((StatusCode::OK, Json(json!({"msg": "TaskDeleted"}))))
	}

main.rs

use axum::{
	extract::{Extension},
	routing::{get, post, put, delete},
	Router,
};

use sqlx::postgres::PgPoolOptions;
use std::net::SocketAddr;
use std::fs;
use anyhow::Context
use tower_http::trace::TraceLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SuscriberInitExt};

#[tokio::main]
async fn main() -> anyhow::Result<()> {

	let env = fs::read_to_string(".env").unwrap();
	let (key, database_url) = env.split_once('=').unwrap();

	assert_eq!(key, "DATABASE_URL");

	tracing_subscriber::registery()
		.with(tracing_subscriber::EnvFilter::new(
			std::env::var("tower_http=trace")
				.unwrap_or_else(|_| "example_tracing_aka_logging=debug,tower_http=debug".into()),
				))
				.with(tracing_subscriber::fmt::layer())
				.init();

	let pool = PgPoolOptions::new()
	.max_connections(50)
	.connect(&dtabase_url)
	.await
	.context("could not connect to database_url")?;

	let app = Router::new()
		.route("/hello", get(root))
		.route("/tasks", get(controllers::task::all_tasks))
		.route("/task", post(controllers::task::new_task))
		.route("/task/:id", get(controllers::task::task))
		.route("/task/:id", put(controllers::task::update_task))
		.route("/task/:id", delete(controllers::task::delete_task))
		.layer(Extension(pool))
		.layer(TraceLayer::new_for_http());

	let addr = SocketAddr::from(([192,168,56,10], 8000));
	println!("listening on {}", addr);
	axum::Server::bind(&addr)
		.serve(app.into_make_service())
		.await?;

		Ok(())
}

async fn root() -> &'static str {
	"Hello, World"
}

Rust axum サンプル

Rust github: https://github.com/tokio-rs/axum

use axum:: {
	routing::{get, post},
	http::StatusCode,
	response::IntoResponse,
	Json, Router,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
	tracing_subscriber::fmt::init();

	let app = Router::new()
		.route("/", get(root))
		.route("/users", post(create_user));

	let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
	axum::server(listener, app).await.unwrap();
}

async fn root() -> &'static str {
	"Hello, World"
}

async fn create_user(
	Json(payload): Json<CreateUser>,
) -> (StatusCode, Json<User>){
	let user = User {
		id: 1337,
		username: payload.username,
	};

	(StatusCode::CREATED, Json(user))
}

#[derive(Deserialize)]
struct CreateUser {
	username: String,
}

#[derive(Serialize)]
struct User {
	id: u64,
	username: String,
}

axum project
https://github.com/tokio-rs/axum/blob/main/ECOSYSTEM.md

Cargo.toml

[dependencies]
axum="0.5"
tokio = { version="1", features-["full"] }

main.rs

use axum::{routing::get, Router};

#[tokio::main]
async fn main(){
	let app = Router::new().route("/",  get(|| async {"hello world!"}));

	axum::Server::bind(&&"0.0.0.0:3000".parse().unwrap())
		.serve(app.into_make_service())
		.await
		.unwrap();
}

workspace Cargo.toml

[workspace]
members = [
	"hello-world"
]

$ cargo new hello-world

[dependencies]
axum = "0.5"
tokio = { version = "1", features = ["full"] }
use axum::{routing::get, Router};
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
	let app = Router::new().route("/", get(handler));

	let addr = SocketAddr::from(([192,168,56,10], 3000));

	axum::Server::bind(&addr)
		.serve(app.into_make_service())
		.await
		.unwrap();
}

async fn handler() -> &'static str {
	"hello world!"
}

/workspace/generate-random-number

[dependencies]
axum = "0.5"
rand = "0.8"
serde = { version = "1", features = ["derive"]}
tokio = { version = "1", features = ["full"]}
use axum::{extract::Query, response::Html, routing::get, Router};
use rand::{thread_rng, Rng};
use serde::Deserialize;
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
	let app = Router::new().route("/", get(handler));

	let addr = SocketAddr::from(([192,168,56,10], 3000));
	println!("listening on {}", addr);
	axum::Server::bind(&addr)
		.serve(app.into_make_service())
		.await
		.unwrap();
}

#[derive(Deserialize)]
struct RangeParameters {
	start: usize,
	end: usize,
}

async fn handler(Query(range): Query<RangeParameters>)-> Html<String> {

	let random_number = thread_rng().gen_range(range.start..range.end);

	Html(format!("<h1>Random Number: {}</h1>", random_number))
}

http://192.168.56.10:3000/?start=50&end=100

use axum::{response::Html, routing::get, Router};
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
	let app = Router::new().route("/", get(handler));

	let addr = SocketAddr::from(([192,168,56,10], 3000));

	axum::Server::bind(&addr)
		.serve(app.into_make_service())
		.await
		.unwrap();
}

async fn handler() -> HTML<&'static str> {
	Html(include_str!("../index.html"))
}