Rust從入門到精通之進階篇:19.Rust 生態系統

Rust 生態系統

Rust 擁有一個豐富而活躍的生態系統,提供了各種庫和框架來支持不同領域的開發。在本章中,我們將探索 Rust 生態系統中的主要組件,了解常用的庫和工具,以及如何在項目中有效地使用它們。

Rust 包管理:Cargo 和 crates.io

Cargo 回顧

Cargo 是 Rust 的構建系統和包管理器,它處理許多任務:

  • 構建代碼(cargo build
  • 運行代碼(cargo run
  • 測試代碼(cargo test
  • 生成文檔(cargo doc
  • 發布庫到 crates.io(cargo publish

crates.io

crates.io 是 Rust 社區的包注冊中心,包含數千個可重用的庫(稱為 crates)。

添加依賴

Cargo.toml 文件中添加依賴:

[dependencies]
serde = "1.0"
serde_json = "1.0"
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }

語義化版本控制

Cargo 使用語義化版本控制(SemVer):

  • "1.0.0" - 精確匹配版本
  • "^1.0.0""1.0" - 兼容 1.0.0 的任何版本(1.0.0 <= 版本 < 2.0.0)
  • "~1.0.0" - 補丁級別更新(1.0.0 <= 版本 < 1.1.0)
  • "*" - 任何版本

特性(Features)

特性允許條件編譯和可選依賴:

[dependencies]
reqwest = { version = "0.11", features = ["json", "blocking"], default-features = false }

工作區(Workspaces)

工作區允許管理多個相關包:

# Cargo.toml
[workspace]
members = ["app","lib_a","lib_b",
]

常用庫概覽

序列化和反序列化:Serde

Serde 是 Rust 的序列化框架,支持多種數據格式。

use serde::{Deserialize, Serialize};#[derive(Serialize, Deserialize, Debug)]
struct Person {name: String,age: u32,emails: Vec<String>,
}fn main() -> Result<(), Box<dyn std::error::Error>> {// 創建數據結構let person = Person {name: String::from("John Doe"),age: 43,emails: vec![String::from("john@example.com")],};// 序列化為 JSONlet json = serde_json::to_string_pretty(&person)?;println!("JSON: {}\n", json);// 反序列化 JSONlet deserialized: Person = serde_json::from_str(&json)?;println!("反序列化: {:?}", deserialized);Ok(())
}

HTTP 客戶端:reqwest

reqwest 是一個易用的 HTTP 客戶端。

use serde::{Deserialize, Serialize};#[derive(Deserialize, Debug)]
struct Todo {userId: i32,id: i32,title: String,completed: bool,
}#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {// 發送 GET 請求let todos: Vec<Todo> = reqwest::Client::new().get("https://jsonplaceholder.typicode.com/todos?_limit=5").send().await?.json().await?;println!("獲取到 {} 個待辦事項:", todos.len());for todo in todos {println!("- {} (完成: {})", todo.title, todo.completed);}// 發送 POST 請求#[derive(Serialize)]struct NewTodo {title: String,completed: bool,userId: i32,}let new_todo = NewTodo {title: String::from("學習 Rust 生態系統"),completed: false,userId: 1,};let response = reqwest::Client::new().post("https://jsonplaceholder.typicode.com/todos").json(&new_todo).send().await?;let created_todo: Todo = response.json().await?;println!("\n創建的待辦事項: {:?}", created_todo);Ok(())
}

命令行參數解析:clap

clap 是一個功能強大的命令行參數解析庫。

use clap::{App, Arg};fn main() {let matches = App::new("My CLI Program").version("1.0").author("Your Name").about("Does awesome things").arg(Arg::new("config").short('c').long("config").value_name("FILE").help("Sets a custom config file").takes_value(true),).arg(Arg::new("verbose").short('v').multiple_occurrences(true).help("Sets the level of verbosity"),).arg(Arg::new("INPUT").help("Sets the input file to use").required(true).index(1),).get_matches();// 獲取參數值let config = matches.value_of("config").unwrap_or("default.conf");println!("使用配置文件: {}", config);let verbose = matches.occurrences_of("verbose");println!("詳細級別: {}", verbose);if let Some(input) = matches.value_of("INPUT") {println!("使用輸入文件: {}", input);}
}

日志記錄:log 和 env_logger

log 提供日志記錄 API,env_logger 提供實現。

use log::{debug, error, info, trace, warn};fn main() {// 初始化日志記錄器env_logger::init();trace!("這是一條跟蹤日志");debug!("這是一條調試日志");info!("這是一條信息日志");warn!("這是一條警告日志");error!("這是一條錯誤日志");// 使用格式化let user = "Alice";info!("用戶 {} 已登錄", user);// 條件日志記錄if let Err(e) = complex_operation() {error!("操作失敗: {}", e);}
}fn complex_operation() -> Result<(), String> {// 模擬操作Err(String::from("示例錯誤"))
}

運行時設置日志級別:

RUST_LOG=debug cargo run

異步運行時:Tokio

Tokio 是 Rust 最流行的異步運行時。

use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {let listener = TcpListener::bind("127.0.0.1:8080").await?;println!("服務器監聽在 127.0.0.1:8080");loop {let (socket, addr) = listener.accept().await?;println!("接受來自 {} 的連接", addr);// 為每個連接生成一個新任務tokio::spawn(async move {if let Err(e) = handle_connection(socket).await {eprintln!("處理連接時出錯: {}", e);}});}
}async fn handle_connection(mut socket: TcpStream) -> Result<(), Box<dyn std::error::Error>> {let mut buffer = [0; 1024];// 讀取數據let n = socket.read(&mut buffer).await?;let message = String::from_utf8_lossy(&buffer[..n]);println!("收到消息: {}", message);// 發送響應socket.write_all(b"Hello from Rust server!").await?;Ok(())
}

Web 框架:Actix Web

Actix Web 是一個高性能的 Web 框架。

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};#[derive(Serialize, Deserialize)]
struct User {name: String,email: String,
}async fn index() -> impl Responder {HttpResponse::Ok().body("Hello world!")
}async fn get_users() -> impl Responder {let users = vec![User {name: "Alice".to_string(),email: "alice@example.com".to_string(),},User {name: "Bob".to_string(),email: "bob@example.com".to_string(),},];HttpResponse::Ok().json(users)
}async fn create_user(user: web::Json<User>) -> impl Responder {println!("創建用戶: {} ({})", user.name, user.email);HttpResponse::Created().json(user.0)
}#[actix_web::main]
async fn main() -> std::io::Result<()> {HttpServer::new(|| {App::new().route("/", web::get().to(index)).route("/users", web::get().to(get_users)).route("/users", web::post().to(create_user))}).bind("127.0.0.1:8080")?.run().await
}

數據庫訪問:Diesel

Diesel 是一個 ORM 和查詢構建器。

// 首先安裝 diesel_cli: cargo install diesel_cli --no-default-features --features sqlite
// 然后初始化: diesel setup --database-url=database.sqlite#[macro_use]
extern crate diesel;use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;// 生成的 schema
mod schema {diesel::table! {users (id) {id -> Integer,name -> Text,email -> Text,}}
}use schema::users;#[derive(Queryable, Debug)]
struct User {id: i32,name: String,email: String,
}#[derive(Insertable)]
#[table_name = "users"]
struct NewUser<'a> {name: &'a str,email: &'a str,
}fn main() -> Result<(), Box<dyn std::error::Error>> {// 建立數據庫連接let conn = SqliteConnection::establish("database.sqlite")?;// 創建用戶let new_user = NewUser {name: "Alice",email: "alice@example.com",};diesel::insert_into(users::table).values(&new_user).execute(&conn)?;// 查詢用戶let results = users::table.limit(5).load::<User>(&conn)?;println!("查詢到 {} 個用戶:", results.len());for user in results {println!("{:?}", user);}Ok(())
}

錯誤處理:anyhow 和 thiserror

anyhow 簡化應用程序錯誤處理,thiserror 簡化庫錯誤定義。

use anyhow::{Context, Result};
use std::fs;
use std::path::Path;fn read_config(path: &Path) -> Result<String> {fs::read_to_string(path).with_context(|| format!("無法讀取配置文件 {}", path.display()))
}fn main() -> Result<()> {let config = read_config(Path::new("config.txt"))?;println!("配置內容: {}", config);Ok(())
}
use thiserror::Error;#[derive(Error, Debug)]
enum DataError {#[error("數據解析錯誤: {0}")]ParseError(String),#[error("IO 錯誤: {0}")]IoError(#[from] std::io::Error),#[error("數據驗證錯誤")]ValidationError {#[source]source: ValidationError,field: String,},
}#[derive(Error, Debug)]
#[error("驗證失敗: {msg}")]
struct ValidationError {msg: String,
}

測試工具:proptest 和 criterion

proptest 用于屬性測試,criterion 用于基準測試。

use proptest::prelude::*;// 被測函數
fn sort_list(mut list: Vec<i32>) -> Vec<i32> {list.sort();list
}proptest! {#[test]fn test_sort_list(list: Vec<i32>) {let sorted = sort_list(list.clone());// 屬性 1: 排序后長度不變prop_assert_eq!(list.len(), sorted.len());// 屬性 2: 排序后元素有序for i in 1..sorted.len() {prop_assert!(sorted[i-1] <= sorted[i]);}// 屬性 3: 排序前后元素集合相同let mut list_copy = list.clone();let mut sorted_copy = sorted.clone();list_copy.sort();prop_assert_eq!(list_copy, sorted_copy);}
}
// benches/my_benchmark.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};fn fibonacci(n: u64) -> u64 {match n {0 => 0,1 => 1,_ => fibonacci(n-1) + fibonacci(n-2),}
}fn criterion_benchmark(c: &mut Criterion) {c.bench_function("fibonacci 20", |b| b.iter(|| fibonacci(black_box(20))));
}criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

領域特定庫

游戲開發:Bevy

Bevy 是一個數據驅動的游戲引擎。

use bevy::prelude::*;fn main() {App::new().add_plugins(DefaultPlugins).add_startup_system(setup).add_system(move_sprite).run();
}fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {// 添加 2D 攝像機commands.spawn_bundle(OrthographicCameraBundle::new_2d());// 添加精靈commands.spawn_bundle(SpriteBundle {texture: asset_server.load("sprite.png"),transform: Transform::from_xyz(0.0, 0.0, 0.0),..Default::default()});
}fn move_sprite(time: Res<Time>, mut query: Query<&mut Transform, With<Sprite>>) {for mut transform in query.iter_mut() {transform.translation.x += 100.0 * time.delta_seconds();}
}

嵌入式開發:embedded-hal

embedded-hal 提供嵌入式設備的硬件抽象層。

#![no_std]
#![no_main]use panic_halt as _;
use cortex_m_rt::entry;
use stm32f1xx_hal::{pac, prelude::*, delay::Delay};#[entry]
fn main() -> ! {// 獲取外設訪問let dp = pac::Peripherals::take().unwrap();let cp = cortex_m::Peripherals::take().unwrap();// 配置時鐘let mut flash = dp.FLASH.constrain();let mut rcc = dp.RCC.constrain();let clocks = rcc.cfgr.freeze(&mut flash.acr);// 配置 GPIOlet mut gpioc = dp.GPIOC.split(&mut rcc.apb2);let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);// 配置延遲let mut delay = Delay::new(cp.SYST, clocks);loop {// 點亮 LEDled.set_low();delay.delay_ms(1000_u16);// 熄滅 LEDled.set_high();delay.delay_ms(1000_u16);}
}

機器學習:tch-rs

tch-rs 提供 PyTorch 的 Rust 綁定。

use tch::{nn, nn::Module, nn::OptimizerConfig, Tensor};fn main() {// 創建模型let vs = nn::VarStore::new(tch::Device::Cpu);let net = nn::seq().add(nn::linear(&vs.root(), 784, 128, Default::default())).add_fn(|xs| xs.relu()).add(nn::linear(&vs.root(), 128, 10, Default::default()));// 創建優化器let mut opt = nn::Adam::default().build(&vs, 1e-3).unwrap();// 創建訓練數據let x = Tensor::rand(&[64, 784], tch::kind::FLOAT_CPU);let y = Tensor::zeros(&[64], tch::kind::INT64_CPU);// 訓練循環for epoch in 1..100 {let loss = net.forward(&x).cross_entropy_for_logits(&y);opt.backward_step(&loss);if epoch % 10 == 0 {println!("Epoch: {}, loss: {}", epoch, f64::from(&loss));}}// 預測let test_x = Tensor::rand(&[10, 784], tch::kind::FLOAT_CPU);let prediction = net.forward(&test_x).argmax(1, false);println!("預測結果: {:?}", Vec::<i64>::from(&prediction));
}

工具和開發環境

代碼格式化:rustfmt

# 安裝
rustup component add rustfmt# 格式化單個文件
rustfmt src/main.rs# 格式化整個項目
cargo fmt

代碼檢查:clippy

# 安裝
rustup component add clippy# 運行檢查
cargo clippy

文檔生成:rustdoc

# 生成文檔
cargo doc --open

依賴分析:cargo-audit

# 安裝
cargo install cargo-audit# 檢查依賴中的安全漏洞
cargo audit

性能分析:flamegraph

# 安裝
cargo install flamegraph# 生成火焰圖
cargo flamegraph

最佳實踐

1. 選擇合適的依賴

評估庫時考慮以下因素:

  • 維護狀態(最近更新時間、未解決的問題)
  • 文檔質量
  • 下載量和使用者
  • 許可證兼容性
  • 依賴樹大小

2. 版本鎖定

在生產環境中鎖定依賴版本:

# 生成 Cargo.lock 文件的確切版本快照
cargo generate-lockfile# 更新到最新兼容版本
cargo update# 更新特定依賴
cargo update -p serde

3. 使用工作區組織大型項目

my_project/
├── Cargo.toml        # 工作區配置
├── common/           # 共享代碼
│   ├── Cargo.toml
│   └── src/
├── backend/          # 后端服務
│   ├── Cargo.toml
│   └── src/
└── frontend/         # 前端應用├── Cargo.toml└── src/

4. 使用特性控制功能

在庫中定義特性:

# Cargo.toml
[features]
default = ["json"]
json = ["serde_json"]
xml = ["serde_xml"][dependencies]
serde = "1.0"
serde_json = { version = "1.0", optional = true }
serde_xml = { version = "0.9", optional = true }

在代碼中使用條件編譯:

#[cfg(feature = "json")]
pub fn parse_json(input: &str) -> Result<Data, Error> {// JSON 解析實現
}#[cfg(feature = "xml")]
pub fn parse_xml(input: &str) -> Result<Data, Error> {// XML 解析實現
}

5. 遵循 Rust API 指南

Rust API 指南 提供了設計 Rust API 的最佳實踐:

  • 使用明確的類型而不是泛型(當合適時)
  • 為公共 API 提供詳細文檔
  • 遵循命名約定(方法、類型、模塊等)
  • 實現常見特質(Debug、Clone、PartialEq 等)

構建真實世界應用

Web API 服務器

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
use sqlx::{postgres::PgPoolOptions, Pool, Postgres};#[derive(Serialize, Deserialize, sqlx::FromRow)]
struct Task {id: Option<i32>,title: String,completed: bool,
}struct AppState {db: Pool<Postgres>,
}async fn get_tasks(data: web::Data<AppState>) -> impl Responder {let tasks = sqlx::query_as::<_, Task>("SELECT * FROM tasks").fetch_all(&data.db).await;match tasks {Ok(tasks) => HttpResponse::Ok().json(tasks),Err(e) => {eprintln!("數據庫錯誤: {}", e);HttpResponse::InternalServerError().finish()}}
}async fn create_task(task: web::Json<Task>, data: web::Data<AppState>) -> impl Responder {let result = sqlx::query_as::<_, Task>("INSERT INTO tasks (title, completed) VALUES ($1, $2) RETURNING id, title, completed",).bind(&task.title).bind(task.completed).fetch_one(&data.db).await;match result {Ok(task) => HttpResponse::Created().json(task),Err(e) => {eprintln!("數據庫錯誤: {}", e);HttpResponse::InternalServerError().finish()}}
}#[actix_web::main]
async fn main() -> std::io::Result<()> {// 設置日志env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));// 連接數據庫let pool = PgPoolOptions::new().max_connections(5).connect("postgres://postgres:password@localhost/tasks_db").await.expect("無法連接到數據庫");// 確保表存在sqlx::query("CREATE TABLE IF NOT EXISTS tasks (id SERIAL PRIMARY KEY,title TEXT NOT NULL,completed BOOLEAN NOT NULL DEFAULT FALSE)").execute(&pool).await.expect("無法創建表");// 啟動 HTTP 服務器HttpServer::new(move || {App::new().app_data(web::Data::new(AppState { db: pool.clone() })).route("/tasks", web::get().to

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/75593.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/75593.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/75593.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

前端面試:如何去衡量用戶操作過程中否卡頓?

衡量用戶在應用中的操作是否卡頓是前端開發中的一個關鍵任務&#xff0c;涉及用戶體驗的各個方面。以下是一些常用的方法和工具&#xff0c;可以幫助你有效地評估用戶操作中的卡頓情況&#xff1a; 1. 使用性能分析工具 瀏覽器開發者工具&#xff1a;大多數現代瀏覽器&#xf…

Python技術棧與數據可視化創意實踐詳解(三)

Python在數據可視化領域憑借豐富的庫和靈活的生態系統&#xff0c;能夠實現從基礎圖表到復雜交互式可視化的全場景覆蓋。以下從技術選型、創意實現到實戰優化進行系統化解析&#xff0c;并提供可直接落地的代碼示例。 一、Python數據可視化技術棧 1. 基礎與統計可視化 Matplotl…

訂票系統|基于Java+vue的火車票訂票系統(源碼+數據庫+文檔)

訂票系統目錄 基于Springbootvue的火車票訂票系統 一、前言 二、系統設計 三、系統功能設計 1會員信息管理 2 車次信息管理 3訂票訂單管理 4留言板管理 四、數據庫設計 五、核心代碼 六、論文參考 七、最新計算機畢設選題推薦 八、源碼獲取&#xff1a; 博主介紹…

Snowflake 算法的實現

snowflake(雪花算法)是一個開源的分布式 ID 生成算法&#xff0c;結果是一個 long 型的 ID。snowflake 算法將 64bit 劃分為多段&#xff0c;分開來標識機器、時間等信息&#xff0c;具體組成結構如下圖所示&#xff1a; snowflake 算法的核心思想是使用 41bit 作為毫秒數&…

C 語言中, scanf 函數在哪些情況下會結束輸入讀取:

在 C 語言中&#xff0c; scanf 函數在以下幾種情況下會結束輸入讀取&#xff1a; &#xff1a; 1. 遇到指定格式匹配失敗&#xff1a; scanf 按照格式字符串要求讀取輸入。當輸入數據格式與格式字符串不匹配時&#xff0c;就會結束讀取。例如 scanf(“%d”, &num) 要求輸…

括號合法題

一、括號合法題 2116. 判斷一個括號字符串是否有效 //采用從左往右和從右往左遍歷的貪心算法&#xff0c;分別保證前綴合法&#xff0c;后綴合法。public boolean canBeValid(String s, String locked) {int ns.length();if (n%21) return false;int num0;// 從左到右掃描&…

圖生生AI商品圖:一鍵更換商品,保留原背景

圖生生AI商品圖工具&#xff0c;推出 “更換商品”功能&#xff0c;只需上傳一張參考圖和自己的商品圖&#xff0c;AI自動完成商品替換&#xff0c;保留原背景&#xff0c;3秒生成專業級電商圖&#xff01;無需PS技能&#xff0c;無需復雜操作&#xff0c;真正實現 “一鍵換商品…

[7-01-03].SpringBoot3集成MinIo

MinIO學習大綱 一、Spingboot整合MinIo 第1步&#xff1a;搭建SpringBoot項目&#xff1a; 第2步&#xff1a;引入minio依賴 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi&q…

Gradle Project import Eclipse

Gradle Project import Eclipse

一些SQL優化經驗(非添加索引版)

SQL 優化核心策略 偽代碼示例&#xff0c;現實比這個復雜 1. 子查詢優化 (1) 避免低效的 IN 和 NOT IN 問題&#xff1a; NOT IN 可能導致全表掃描&#xff0c;尤其是子查詢結果集較大時。 優化方案&#xff1a; 替換為 LEFT JOIN&#xff1a; -- 原查詢&#xff08;低效&am…

<項目> 高并發服務器的HTTP協議支持

目錄 HTTP模塊 模塊劃分與介紹 模塊實現 Util模塊 HTTPRequest模塊 HTTPResponse模塊 HTTPContext模塊 ParseHttpLine RecvHttpLine RecvHttpHead ParseHttpHead RecvHttpBody 對外接口 HttpServer模塊 OnConnected OnMessage Route IsFileHandler FileHandler Dispatcher …

基于Spring Boot + Vue的銀行管理系統設計與實現

基于Spring Boot Vue的銀行管理系統設計與實現 一、引言 隨著金融數字化進程加速&#xff0c;傳統銀行業務向線上化轉型成為必然趨勢。本文設計并實現了一套基于Spring Boot Vue的銀行管理系統&#xff0c;通過模塊化架構滿足用戶、銀行職員、管理員三類角色的核心業務需求…

微軟提出 Logic-RL:基于規則的強化學習釋放大語言模型推理能力

? 更多 LLM 架構文章點擊查看&#xff1a; LLM 架構專欄 大模型架構專欄文章閱讀指南 1. AI 智能體&#xff0c;顛覆還是賦能&#xff1f;一文讀懂&#xff01; 2. 1W8000 字 解鎖 AI 高效運作密碼&#xff1a;工作流與智能體如何協同&#xff1f; 3. 萬字深度剖析 AI 代理&am…

STM32八股【1】-----啟動流程和startup文件理解

啟動流程 知識點 MCU 上電復位。MSP從向量表第0個地址讀取一個32位&#xff08;2字節&#xff09;的值并保存&#xff0c;該值為棧頂地址。PC計數器從第1個地址讀取一個兩字節的值并保存&#xff0c;該值為程序入口&#xff0c;一般是Reset_Handler。想了解FLASH地址映射可以…

詳解c++20的協程,自定義可等待對象,生成器詳解

協程 c20的協程三大標簽&#xff1a;“性能之優秀”&#xff0c;“開發之靈活”&#xff0c;“門檻之高” 在講解c的協程使用前&#xff0c;我們需要先明白協程是什么&#xff0c;協程可以理解為用戶態的線程&#xff0c;它需要由程序來進行調度&#xff0c;如上下文切換與調…

JavaEE企業級開發 延遲雙刪+版本號機制(樂觀鎖) 事務保證redis和mysql的數據一致性 示例

提醒 要求了解或者熟練掌握以下知識點 spring 事務mysql 臟讀如何保證緩存和數據庫數據一致性延遲雙刪分布式鎖并發編程 原子操作類 前言 在起草這篇博客之前 我做了點功課 這邊我寫的是一個示例代碼 數據層都寫成了 mock 的形式(來源于 JUnit5) // Dduo import java.u…

A2 最佳學習方法

記錄自己想法的最好理由是發現自己的想法&#xff0c;并將其組織成可傳播的形式 (The best reason for recording what one thinks is to discover what one thinks and to organize it in transmittable form.) Prof Ackoff 經驗之談&#xff1a; 做培訓或者寫文章&#xff…

嵌入式硬件工程師從小白到入門-PCB繪制(二)

PCB繪制從小白到入門&#xff1a;知識點速通與面試指南 一、PCB設計核心流程 需求分析 明確電路功能&#xff08;如電源、信號處理、通信&#xff09;。確定關鍵參數&#xff08;電壓、電流、頻率、接口類型&#xff09;。 原理圖設計 元器件選型&#xff1a;匹配封裝、電壓、…

vue創建子組件步驟及注意事項

在 Vue 中創建子組件需要遵循組件化開發的核心原則&#xff0c;并注意數據流、通信機制、復用性等關鍵點。以下是詳細步驟和注意事項&#xff0c;結合代碼示例說明&#xff1a; 一、創建子組件的步驟 1. 定義子組件 創建一個 .vue 文件&#xff08;單文件組件&#xff09;&am…

Cocos Creator版本發布時間線

官網找不到&#xff0c;DeepSeek給的答案&#xff0c;這里做個記錄。 Cocos Creator 1.x 系列 發布時間&#xff1a;2016 年 - 2018 年 1.0&#xff08;2016 年 3 月&#xff09;&#xff1a; 首個正式版本&#xff0c;基于 Cocos2d-x 的 2D 游戲開發工具鏈&#xff0c;集成可…